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       case ON_DANGEROUS_DOWNLOAD_QUIT:
    176         DownloadInFinalState(download);
    177         break;
    178 
    179       default:
    180         NOTREACHED();
    181     }
    182   }
    183 
    184   if (IsDownloadInFinalState(download))
    185     DownloadInFinalState(download);
    186 }
    187 
    188 size_t DownloadTestObserver::NumDangerousDownloadsSeen() const {
    189   return dangerous_downloads_seen_.size();
    190 }
    191 
    192 size_t DownloadTestObserver::NumDownloadsSeenInState(
    193     DownloadItem::DownloadState state) const {
    194   StateMap::const_iterator it = states_observed_.find(state);
    195 
    196   if (it == states_observed_.end())
    197     return 0;
    198 
    199   return it->second;
    200 }
    201 
    202 void DownloadTestObserver::DownloadInFinalState(DownloadItem* download) {
    203   if (finished_downloads_.find(download) != finished_downloads_.end()) {
    204     // We've already seen the final state on this download.
    205     return;
    206   }
    207 
    208   // Record the transition.
    209   finished_downloads_.insert(download);
    210 
    211   // Record the state.
    212   states_observed_[download->GetState()]++;  // Initializes to 0 the first time.
    213 
    214   SignalIfFinished();
    215 }
    216 
    217 void DownloadTestObserver::SignalIfFinished() {
    218   if (waiting_ && IsFinished())
    219     base::MessageLoopForUI::current()->Quit();
    220 }
    221 
    222 void DownloadTestObserver::AcceptDangerousDownload(uint32 download_id) {
    223   // Download manager was shutdown before the UI thread could accept the
    224   // download.
    225   if (!download_manager_)
    226     return;
    227   DownloadItem* download = download_manager_->GetDownload(download_id);
    228   if (download && !download->IsDone())
    229     download->ValidateDangerousDownload();
    230 }
    231 
    232 void DownloadTestObserver::DenyDangerousDownload(uint32 download_id) {
    233   // Download manager was shutdown before the UI thread could deny the
    234   // download.
    235   if (!download_manager_)
    236     return;
    237   DownloadItem* download = download_manager_->GetDownload(download_id);
    238   if (download && !download->IsDone())
    239     download->Remove();
    240 }
    241 
    242 DownloadTestObserverTerminal::DownloadTestObserverTerminal(
    243     DownloadManager* download_manager,
    244     size_t wait_count,
    245     DangerousDownloadAction dangerous_download_action)
    246         : DownloadTestObserver(download_manager,
    247                                wait_count,
    248                                dangerous_download_action) {
    249   // You can't rely on overriden virtual functions in a base class constructor;
    250   // the virtual function table hasn't been set up yet.  So, we have to do any
    251   // work that depends on those functions in the derived class constructor
    252   // instead.  In this case, it's because of |IsDownloadInFinalState()|.
    253   Init();
    254 }
    255 
    256 DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
    257 }
    258 
    259 
    260 bool DownloadTestObserverTerminal::IsDownloadInFinalState(
    261     DownloadItem* download) {
    262   return download->IsDone();
    263 }
    264 
    265 DownloadTestObserverInProgress::DownloadTestObserverInProgress(
    266     DownloadManager* download_manager,
    267     size_t wait_count)
    268         : DownloadTestObserver(download_manager,
    269                                wait_count,
    270                                ON_DANGEROUS_DOWNLOAD_ACCEPT) {
    271   // You can't override virtual functions in a base class constructor; the
    272   // virtual function table hasn't been set up yet.  So, we have to do any
    273   // work that depends on those functions in the derived class constructor
    274   // instead.  In this case, it's because of |IsDownloadInFinalState()|.
    275   Init();
    276 }
    277 
    278 DownloadTestObserverInProgress::~DownloadTestObserverInProgress() {
    279 }
    280 
    281 
    282 bool DownloadTestObserverInProgress::IsDownloadInFinalState(
    283     DownloadItem* download) {
    284   return (download->GetState() == DownloadItem::IN_PROGRESS) &&
    285       !download->GetTargetFilePath().empty();
    286 }
    287 
    288 DownloadTestObserverInterrupted::DownloadTestObserverInterrupted(
    289     DownloadManager* download_manager,
    290     size_t wait_count,
    291     DangerousDownloadAction dangerous_download_action)
    292         : DownloadTestObserver(download_manager,
    293                                wait_count,
    294                                dangerous_download_action) {
    295   // You can't rely on overriden virtual functions in a base class constructor;
    296   // the virtual function table hasn't been set up yet.  So, we have to do any
    297   // work that depends on those functions in the derived class constructor
    298   // instead.  In this case, it's because of |IsDownloadInFinalState()|.
    299   Init();
    300 }
    301 
    302 DownloadTestObserverInterrupted::~DownloadTestObserverInterrupted() {
    303 }
    304 
    305 
    306 bool DownloadTestObserverInterrupted::IsDownloadInFinalState(
    307     DownloadItem* download) {
    308   return download->GetState() == DownloadItem::INTERRUPTED;
    309 }
    310 
    311 DownloadTestFlushObserver::DownloadTestFlushObserver(
    312     DownloadManager* download_manager)
    313     : download_manager_(download_manager),
    314       waiting_for_zero_inprogress_(true) {}
    315 
    316 void DownloadTestFlushObserver::WaitForFlush() {
    317   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    318   download_manager_->AddObserver(this);
    319   // The wait condition may have been met before WaitForFlush() was called.
    320   CheckDownloadsInProgress(true);
    321   BrowserThread::GetBlockingPool()->FlushForTesting();
    322   RunMessageLoop();
    323 }
    324 
    325 void DownloadTestFlushObserver::OnDownloadCreated(
    326     DownloadManager* manager,
    327     DownloadItem* item) {
    328   CheckDownloadsInProgress(true);
    329 }
    330 
    331 void DownloadTestFlushObserver::OnDownloadDestroyed(DownloadItem* download) {
    332   // Stop observing.  Do not do anything with it, as it is about to be gone.
    333   DownloadSet::iterator it = downloads_observed_.find(download);
    334   ASSERT_TRUE(it != downloads_observed_.end());
    335   downloads_observed_.erase(it);
    336   download->RemoveObserver(this);
    337 }
    338 
    339 void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) {
    340   // No change in DownloadItem set on manager.
    341   CheckDownloadsInProgress(false);
    342 }
    343 
    344 DownloadTestFlushObserver::~DownloadTestFlushObserver() {
    345   download_manager_->RemoveObserver(this);
    346   for (DownloadSet::iterator it = downloads_observed_.begin();
    347        it != downloads_observed_.end(); ++it) {
    348     (*it)->RemoveObserver(this);
    349   }
    350 }
    351 
    352 // If we're waiting for that flush point, check the number
    353 // of downloads in the IN_PROGRESS state and take appropriate
    354 // action.  If requested, also observes all downloads while iterating.
    355 void DownloadTestFlushObserver::CheckDownloadsInProgress(
    356     bool observe_downloads) {
    357   if (waiting_for_zero_inprogress_) {
    358     int count = 0;
    359 
    360     std::vector<DownloadItem*> downloads;
    361     download_manager_->GetAllDownloads(&downloads);
    362     for (std::vector<DownloadItem*>::iterator it = downloads.begin();
    363          it != downloads.end(); ++it) {
    364       if ((*it)->GetState() == DownloadItem::IN_PROGRESS)
    365         count++;
    366       if (observe_downloads) {
    367         if (downloads_observed_.find(*it) == downloads_observed_.end()) {
    368           (*it)->AddObserver(this);
    369           downloads_observed_.insert(*it);
    370         }
    371         // Download items are forever, and we don't want to make
    372         // assumptions about future state transitions, so once we
    373         // start observing them, we don't stop until destruction.
    374       }
    375     }
    376 
    377     if (count == 0) {
    378       waiting_for_zero_inprogress_ = false;
    379       // Stop observing DownloadItems.  We maintain the observation
    380       // of DownloadManager so that we don't have to independently track
    381       // whether we are observing it for conditional destruction.
    382       for (DownloadSet::iterator it = downloads_observed_.begin();
    383            it != downloads_observed_.end(); ++it) {
    384         (*it)->RemoveObserver(this);
    385       }
    386       downloads_observed_.clear();
    387 
    388       // Trigger next step.  We need to go past the IO thread twice, as
    389       // there's a self-task posting in the IO thread cancel path.
    390       BrowserThread::PostTask(
    391           BrowserThread::FILE, FROM_HERE,
    392           base::Bind(&DownloadTestFlushObserver::PingFileThread, this, 2));
    393     }
    394   }
    395 }
    396 
    397 void DownloadTestFlushObserver::PingFileThread(int cycle) {
    398   BrowserThread::PostTask(
    399       BrowserThread::IO, FROM_HERE,
    400       base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle));
    401 }
    402 
    403 void DownloadTestFlushObserver::PingIOThread(int cycle) {
    404   if (--cycle) {
    405     BrowserThread::PostTask(
    406         BrowserThread::UI, FROM_HERE,
    407         base::Bind(&DownloadTestFlushObserver::PingFileThread, this, cycle));
    408   } else {
    409     BrowserThread::PostTask(
    410         BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
    411   }
    412 }
    413 
    414 DownloadTestItemCreationObserver::DownloadTestItemCreationObserver()
    415     : download_id_(DownloadItem::kInvalidId),
    416       interrupt_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
    417       called_back_count_(0),
    418       waiting_(false) {
    419 }
    420 
    421 DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
    422 }
    423 
    424 void DownloadTestItemCreationObserver::WaitForDownloadItemCreation() {
    425   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    426 
    427   if (called_back_count_ == 0) {
    428     waiting_ = true;
    429     RunMessageLoop();
    430     waiting_ = false;
    431   }
    432 }
    433 
    434 void DownloadTestItemCreationObserver::DownloadItemCreationCallback(
    435     DownloadItem* item,
    436     DownloadInterruptReason interrupt_reason) {
    437   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    438 
    439   if (item)
    440     download_id_ = item->GetId();
    441   interrupt_reason_ = interrupt_reason;
    442   ++called_back_count_;
    443   DCHECK_EQ(1u, called_back_count_);
    444 
    445   if (waiting_)
    446     base::MessageLoopForUI::current()->Quit();
    447 }
    448 
    449 const DownloadUrlParameters::OnStartedCallback
    450     DownloadTestItemCreationObserver::callback() {
    451   return base::Bind(
    452       &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this);
    453 }
    454 
    455 }  // namespace content
    456