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 19namespace content { 20 21DownloadUpdatedObserver::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 30DownloadUpdatedObserver::~DownloadUpdatedObserver() { 31 if (item_) 32 item_->RemoveObserver(this); 33} 34 35bool 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 47void 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 55void 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 63DownloadTestObserver::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 75DownloadTestObserver::~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 84void 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 96void DownloadTestObserver::ManagerGoingDown(DownloadManager* manager) { 97 CHECK_EQ(manager, download_manager_); 98 download_manager_ = NULL; 99 SignalIfFinished(); 100} 101 102void DownloadTestObserver::WaitForFinished() { 103 if (!IsFinished()) { 104 waiting_ = true; 105 RunMessageLoop(); 106 waiting_ = false; 107 } 108} 109 110bool DownloadTestObserver::IsFinished() const { 111 return (finished_downloads_.size() - finished_downloads_at_construction_ >= 112 wait_count_) || (download_manager_ == NULL); 113} 114 115void 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 130void 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 138void 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 184size_t DownloadTestObserver::NumDangerousDownloadsSeen() const { 185 return dangerous_downloads_seen_.size(); 186} 187 188size_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 198void 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 213void DownloadTestObserver::SignalIfFinished() { 214 if (waiting_ && IsFinished()) 215 base::MessageLoopForUI::current()->Quit(); 216} 217 218void 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 228void 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 238DownloadTestObserverTerminal::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 252DownloadTestObserverTerminal::~DownloadTestObserverTerminal() { 253} 254 255 256bool DownloadTestObserverTerminal::IsDownloadInFinalState( 257 DownloadItem* download) { 258 return download->IsDone(); 259} 260 261DownloadTestObserverInProgress::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 274DownloadTestObserverInProgress::~DownloadTestObserverInProgress() { 275} 276 277 278bool DownloadTestObserverInProgress::IsDownloadInFinalState( 279 DownloadItem* download) { 280 return (download->GetState() == DownloadItem::IN_PROGRESS) && 281 !download->GetTargetFilePath().empty(); 282} 283 284DownloadTestObserverInterrupted::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 298DownloadTestObserverInterrupted::~DownloadTestObserverInterrupted() { 299} 300 301 302bool DownloadTestObserverInterrupted::IsDownloadInFinalState( 303 DownloadItem* download) { 304 return download->GetState() == DownloadItem::INTERRUPTED; 305} 306 307DownloadTestFlushObserver::DownloadTestFlushObserver( 308 DownloadManager* download_manager) 309 : download_manager_(download_manager), 310 waiting_for_zero_inprogress_(true) {} 311 312void 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 321void DownloadTestFlushObserver::OnDownloadCreated( 322 DownloadManager* manager, 323 DownloadItem* item) { 324 CheckDownloadsInProgress(true); 325} 326 327void 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 335void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) { 336 // No change in DownloadItem set on manager. 337 CheckDownloadsInProgress(false); 338} 339 340DownloadTestFlushObserver::~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. 351void 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 393void DownloadTestFlushObserver::PingFileThread(int cycle) { 394 BrowserThread::PostTask( 395 BrowserThread::IO, FROM_HERE, 396 base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle)); 397} 398 399void 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 410DownloadTestItemCreationObserver::DownloadTestItemCreationObserver() 411 : download_id_(DownloadItem::kInvalidId), 412 error_(net::OK), 413 called_back_count_(0), 414 waiting_(false) { 415} 416 417DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() { 418} 419 420void 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 430void 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 445const DownloadUrlParameters::OnStartedCallback 446 DownloadTestItemCreationObserver::callback() { 447 return base::Bind( 448 &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this); 449} 450 451} // namespace content 452