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