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/download_test_observer.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/stl_util.h"
     13 #include "base/threading/sequenced_worker_pool.h"
     14 #include "content/public/browser/browser_thread.h"
     15 #include "content/public/browser/download_url_parameters.h"
     16 #include "content/public/test/test_utils.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 namespace content {
     20 
     21 DownloadUpdatedObserver::DownloadUpdatedObserver(
     22     DownloadItem* item, DownloadUpdatedObserver::EventFilter filter)
     23     : item_(item),
     24       filter_(filter),
     25       waiting_(false),
     26       event_seen_(false) {
     27   item->AddObserver(this);
     28 }
     29 
     30 DownloadUpdatedObserver::~DownloadUpdatedObserver() {
     31   if (item_)
     32     item_->RemoveObserver(this);
     33 }
     34 
     35 bool DownloadUpdatedObserver::WaitForEvent() {
     36   if (item_ && filter_.Run(item_))
     37     event_seen_ = true;
     38   if (event_seen_)
     39     return true;
     40 
     41   waiting_ = true;
     42   RunMessageLoop();
     43   waiting_ = false;
     44   return event_seen_;
     45 }
     46 
     47 void DownloadUpdatedObserver::OnDownloadUpdated(DownloadItem* item) {
     48   DCHECK_EQ(item_, item);
     49   if (filter_.Run(item_))
     50     event_seen_ = true;
     51   if (waiting_ && event_seen_)
     52     base::MessageLoopForUI::current()->Quit();
     53 }
     54 
     55 void DownloadUpdatedObserver::OnDownloadDestroyed(DownloadItem* item) {
     56   DCHECK_EQ(item_, item);
     57   item_->RemoveObserver(this);
     58   item_ = NULL;
     59   if (waiting_)
     60     base::MessageLoopForUI::current()->Quit();
     61 }
     62 
     63 DownloadTestObserver::DownloadTestObserver(
     64     DownloadManager* download_manager,
     65     size_t wait_count,
     66     DangerousDownloadAction dangerous_download_action)
     67     : download_manager_(download_manager),
     68       wait_count_(wait_count),
     69       finished_downloads_at_construction_(0),
     70       waiting_(false),
     71       dangerous_download_action_(dangerous_download_action),
     72       weak_factory_(this) {
     73 }
     74 
     75 DownloadTestObserver::~DownloadTestObserver() {
     76   for (DownloadSet::iterator it = downloads_observed_.begin();
     77        it != downloads_observed_.end(); ++it)
     78     (*it)->RemoveObserver(this);
     79 
     80   if (download_manager_)
     81     download_manager_->RemoveObserver(this);
     82 }
     83 
     84 void DownloadTestObserver::Init() {
     85   download_manager_->AddObserver(this);
     86   std::vector<DownloadItem*> downloads;
     87   download_manager_->GetAllDownloads(&downloads);
     88   for (std::vector<DownloadItem*>::iterator it = downloads.begin();
     89        it != downloads.end(); ++it) {
     90     OnDownloadCreated(download_manager_, *it);
     91   }
     92   finished_downloads_at_construction_ = finished_downloads_.size();
     93   states_observed_.clear();
     94 }
     95 
     96 void DownloadTestObserver::ManagerGoingDown(DownloadManager* manager) {
     97   CHECK_EQ(manager, download_manager_);
     98   download_manager_ = NULL;
     99   SignalIfFinished();
    100 }
    101 
    102 void DownloadTestObserver::WaitForFinished() {
    103   if (!IsFinished()) {
    104     waiting_ = true;
    105     RunMessageLoop();
    106     waiting_ = false;
    107   }
    108 }
    109 
    110 bool DownloadTestObserver::IsFinished() const {
    111   return (finished_downloads_.size() - finished_downloads_at_construction_ >=
    112           wait_count_) || (download_manager_ == NULL);
    113 }
    114 
    115 void DownloadTestObserver::OnDownloadCreated(
    116     DownloadManager* manager,
    117     DownloadItem* item) {
    118   // NOTE: This method is called both by DownloadManager when a download is
    119   // created as well as in DownloadTestObserver::Init() for downloads that
    120   // existed before |this| was created.
    121   OnDownloadUpdated(item);
    122   DownloadSet::const_iterator finished_it(finished_downloads_.find(item));
    123   // If it isn't finished, start observing it.
    124   if (finished_it == finished_downloads_.end()) {
    125     item->AddObserver(this);
    126     downloads_observed_.insert(item);
    127   }
    128 }
    129 
    130 void DownloadTestObserver::OnDownloadDestroyed(DownloadItem* download) {
    131   // Stop observing.  Do not do anything with it, as it is about to be gone.
    132   DownloadSet::iterator it = downloads_observed_.find(download);
    133   ASSERT_TRUE(it != downloads_observed_.end());
    134   downloads_observed_.erase(it);
    135   download->RemoveObserver(this);
    136 }
    137 
    138 void DownloadTestObserver::OnDownloadUpdated(DownloadItem* download) {
    139   // Real UI code gets the user's response after returning from the observer.
    140   if (download->IsDangerous() &&
    141       !ContainsKey(dangerous_downloads_seen_, download->GetId())) {
    142     dangerous_downloads_seen_.insert(download->GetId());
    143 
    144     // Calling ValidateDangerousDownload() at this point will
    145     // cause the download to be completed twice.  Do what the real UI
    146     // code does: make the call as a delayed task.
    147     switch (dangerous_download_action_) {
    148       case ON_DANGEROUS_DOWNLOAD_ACCEPT:
    149         // Fake user click on "Accept".  Delay the actual click, as the
    150         // real UI would.
    151         BrowserThread::PostTask(
    152             BrowserThread::UI, FROM_HERE,
    153             base::Bind(&DownloadTestObserver::AcceptDangerousDownload,
    154                        weak_factory_.GetWeakPtr(),
    155                        download->GetId()));
    156         break;
    157 
    158       case ON_DANGEROUS_DOWNLOAD_DENY:
    159         // Fake a user click on "Deny".  Delay the actual click, as the
    160         // real UI would.
    161         BrowserThread::PostTask(
    162             BrowserThread::UI, FROM_HERE,
    163             base::Bind(&DownloadTestObserver::DenyDangerousDownload,
    164                        weak_factory_.GetWeakPtr(),
    165                        download->GetId()));
    166         break;
    167 
    168       case ON_DANGEROUS_DOWNLOAD_FAIL:
    169         ADD_FAILURE() << "Unexpected dangerous download item.";
    170         break;
    171 
    172       case ON_DANGEROUS_DOWNLOAD_IGNORE:
    173         break;
    174 
    175       default:
    176         NOTREACHED();
    177     }
    178   }
    179 
    180   if (IsDownloadInFinalState(download))
    181     DownloadInFinalState(download);
    182 }
    183 
    184 size_t DownloadTestObserver::NumDangerousDownloadsSeen() const {
    185   return dangerous_downloads_seen_.size();
    186 }
    187 
    188 size_t DownloadTestObserver::NumDownloadsSeenInState(
    189     DownloadItem::DownloadState state) const {
    190   StateMap::const_iterator it = states_observed_.find(state);
    191 
    192   if (it == states_observed_.end())
    193     return 0;
    194 
    195   return it->second;
    196 }
    197 
    198 void DownloadTestObserver::DownloadInFinalState(DownloadItem* download) {
    199   if (finished_downloads_.find(download) != finished_downloads_.end()) {
    200     // We've already seen the final state on this download.
    201     return;
    202   }
    203 
    204   // Record the transition.
    205   finished_downloads_.insert(download);
    206 
    207   // Record the state.
    208   states_observed_[download->GetState()]++;  // Initializes to 0 the first time.
    209 
    210   SignalIfFinished();
    211 }
    212 
    213 void DownloadTestObserver::SignalIfFinished() {
    214   if (waiting_ && IsFinished())
    215     base::MessageLoopForUI::current()->Quit();
    216 }
    217 
    218 void DownloadTestObserver::AcceptDangerousDownload(uint32 download_id) {
    219   // Download manager was shutdown before the UI thread could accept the
    220   // download.
    221   if (!download_manager_)
    222     return;
    223   DownloadItem* download = download_manager_->GetDownload(download_id);
    224   if (download && !download->IsDone())
    225     download->ValidateDangerousDownload();
    226 }
    227 
    228 void DownloadTestObserver::DenyDangerousDownload(uint32 download_id) {
    229   // Download manager was shutdown before the UI thread could deny the
    230   // download.
    231   if (!download_manager_)
    232     return;
    233   DownloadItem* download = download_manager_->GetDownload(download_id);
    234   if (download && !download->IsDone())
    235     download->Remove();
    236 }
    237 
    238 DownloadTestObserverTerminal::DownloadTestObserverTerminal(
    239     DownloadManager* download_manager,
    240     size_t wait_count,
    241     DangerousDownloadAction dangerous_download_action)
    242         : DownloadTestObserver(download_manager,
    243                                wait_count,
    244                                dangerous_download_action) {
    245   // You can't rely on overriden virtual functions in a base class constructor;
    246   // the virtual function table hasn't been set up yet.  So, we have to do any
    247   // work that depends on those functions in the derived class constructor
    248   // instead.  In this case, it's because of |IsDownloadInFinalState()|.
    249   Init();
    250 }
    251 
    252 DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
    253 }
    254 
    255 
    256 bool DownloadTestObserverTerminal::IsDownloadInFinalState(
    257     DownloadItem* download) {
    258   return download->IsDone();
    259 }
    260 
    261 DownloadTestObserverInProgress::DownloadTestObserverInProgress(
    262     DownloadManager* download_manager,
    263     size_t wait_count)
    264         : DownloadTestObserver(download_manager,
    265                                wait_count,
    266                                ON_DANGEROUS_DOWNLOAD_ACCEPT) {
    267   // You can't override virtual functions in a base class constructor; the
    268   // virtual function table hasn't been set up yet.  So, we have to do any
    269   // work that depends on those functions in the derived class constructor
    270   // instead.  In this case, it's because of |IsDownloadInFinalState()|.
    271   Init();
    272 }
    273 
    274 DownloadTestObserverInProgress::~DownloadTestObserverInProgress() {
    275 }
    276 
    277 
    278 bool DownloadTestObserverInProgress::IsDownloadInFinalState(
    279     DownloadItem* download) {
    280   return (download->GetState() == DownloadItem::IN_PROGRESS) &&
    281       !download->GetTargetFilePath().empty();
    282 }
    283 
    284 DownloadTestObserverInterrupted::DownloadTestObserverInterrupted(
    285     DownloadManager* download_manager,
    286     size_t wait_count,
    287     DangerousDownloadAction dangerous_download_action)
    288         : DownloadTestObserver(download_manager,
    289                                wait_count,
    290                                dangerous_download_action) {
    291   // You can't rely on overriden virtual functions in a base class constructor;
    292   // the virtual function table hasn't been set up yet.  So, we have to do any
    293   // work that depends on those functions in the derived class constructor
    294   // instead.  In this case, it's because of |IsDownloadInFinalState()|.
    295   Init();
    296 }
    297 
    298 DownloadTestObserverInterrupted::~DownloadTestObserverInterrupted() {
    299 }
    300 
    301 
    302 bool DownloadTestObserverInterrupted::IsDownloadInFinalState(
    303     DownloadItem* download) {
    304   return download->GetState() == DownloadItem::INTERRUPTED;
    305 }
    306 
    307 DownloadTestFlushObserver::DownloadTestFlushObserver(
    308     DownloadManager* download_manager)
    309     : download_manager_(download_manager),
    310       waiting_for_zero_inprogress_(true) {}
    311 
    312 void DownloadTestFlushObserver::WaitForFlush() {
    313   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    314   download_manager_->AddObserver(this);
    315   // The wait condition may have been met before WaitForFlush() was called.
    316   CheckDownloadsInProgress(true);
    317   BrowserThread::GetBlockingPool()->FlushForTesting();
    318   RunMessageLoop();
    319 }
    320 
    321 void DownloadTestFlushObserver::OnDownloadCreated(
    322     DownloadManager* manager,
    323     DownloadItem* item) {
    324   CheckDownloadsInProgress(true);
    325 }
    326 
    327 void DownloadTestFlushObserver::OnDownloadDestroyed(DownloadItem* download) {
    328   // Stop observing.  Do not do anything with it, as it is about to be gone.
    329   DownloadSet::iterator it = downloads_observed_.find(download);
    330   ASSERT_TRUE(it != downloads_observed_.end());
    331   downloads_observed_.erase(it);
    332   download->RemoveObserver(this);
    333 }
    334 
    335 void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) {
    336   // No change in DownloadItem set on manager.
    337   CheckDownloadsInProgress(false);
    338 }
    339 
    340 DownloadTestFlushObserver::~DownloadTestFlushObserver() {
    341   download_manager_->RemoveObserver(this);
    342   for (DownloadSet::iterator it = downloads_observed_.begin();
    343        it != downloads_observed_.end(); ++it) {
    344     (*it)->RemoveObserver(this);
    345   }
    346 }
    347 
    348 // If we're waiting for that flush point, check the number
    349 // of downloads in the IN_PROGRESS state and take appropriate
    350 // action.  If requested, also observes all downloads while iterating.
    351 void DownloadTestFlushObserver::CheckDownloadsInProgress(
    352     bool observe_downloads) {
    353   if (waiting_for_zero_inprogress_) {
    354     int count = 0;
    355 
    356     std::vector<DownloadItem*> downloads;
    357     download_manager_->GetAllDownloads(&downloads);
    358     for (std::vector<DownloadItem*>::iterator it = downloads.begin();
    359          it != downloads.end(); ++it) {
    360       if ((*it)->GetState() == DownloadItem::IN_PROGRESS)
    361         count++;
    362       if (observe_downloads) {
    363         if (downloads_observed_.find(*it) == downloads_observed_.end()) {
    364           (*it)->AddObserver(this);
    365           downloads_observed_.insert(*it);
    366         }
    367         // Download items are forever, and we don't want to make
    368         // assumptions about future state transitions, so once we
    369         // start observing them, we don't stop until destruction.
    370       }
    371     }
    372 
    373     if (count == 0) {
    374       waiting_for_zero_inprogress_ = false;
    375       // Stop observing DownloadItems.  We maintain the observation
    376       // of DownloadManager so that we don't have to independently track
    377       // whether we are observing it for conditional destruction.
    378       for (DownloadSet::iterator it = downloads_observed_.begin();
    379            it != downloads_observed_.end(); ++it) {
    380         (*it)->RemoveObserver(this);
    381       }
    382       downloads_observed_.clear();
    383 
    384       // Trigger next step.  We need to go past the IO thread twice, as
    385       // there's a self-task posting in the IO thread cancel path.
    386       BrowserThread::PostTask(
    387           BrowserThread::FILE, FROM_HERE,
    388           base::Bind(&DownloadTestFlushObserver::PingFileThread, this, 2));
    389     }
    390   }
    391 }
    392 
    393 void DownloadTestFlushObserver::PingFileThread(int cycle) {
    394   BrowserThread::PostTask(
    395       BrowserThread::IO, FROM_HERE,
    396       base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle));
    397 }
    398 
    399 void DownloadTestFlushObserver::PingIOThread(int cycle) {
    400   if (--cycle) {
    401     BrowserThread::PostTask(
    402         BrowserThread::UI, FROM_HERE,
    403         base::Bind(&DownloadTestFlushObserver::PingFileThread, this, cycle));
    404   } else {
    405     BrowserThread::PostTask(
    406         BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
    407   }
    408 }
    409 
    410 DownloadTestItemCreationObserver::DownloadTestItemCreationObserver()
    411     : download_id_(DownloadItem::kInvalidId),
    412       error_(net::OK),
    413       called_back_count_(0),
    414       waiting_(false) {
    415 }
    416 
    417 DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
    418 }
    419 
    420 void DownloadTestItemCreationObserver::WaitForDownloadItemCreation() {
    421   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    422 
    423   if (called_back_count_ == 0) {
    424     waiting_ = true;
    425     RunMessageLoop();
    426     waiting_ = false;
    427   }
    428 }
    429 
    430 void DownloadTestItemCreationObserver::DownloadItemCreationCallback(
    431     DownloadItem* item,
    432     net::Error error) {
    433   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    434 
    435   if (item)
    436     download_id_ = item->GetId();
    437   error_ = error;
    438   ++called_back_count_;
    439   DCHECK_EQ(1u, called_back_count_);
    440 
    441   if (waiting_)
    442     base::MessageLoopForUI::current()->Quit();
    443 }
    444 
    445 const DownloadUrlParameters::OnStartedCallback
    446     DownloadTestItemCreationObserver::callback() {
    447   return base::Bind(
    448       &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this);
    449 }
    450 
    451 }  // namespace content
    452