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