Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / public / test / download_test_observer.cc
blob5873df027e24a64a22a2fdaed112b58a55607cdc
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.
5 #include "content/public/test/download_test_observer.h"
7 #include <vector>
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"
19 namespace content {
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);
30 DownloadUpdatedObserver::~DownloadUpdatedObserver() {
31 if (item_)
32 item_->RemoveObserver(this);
35 bool DownloadUpdatedObserver::WaitForEvent() {
36 if (item_ && filter_.Run(item_))
37 event_seen_ = true;
38 if (event_seen_)
39 return true;
41 waiting_ = true;
42 RunMessageLoop();
43 waiting_ = false;
44 return event_seen_;
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();
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();
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) {
75 DownloadTestObserver::~DownloadTestObserver() {
76 for (DownloadSet::iterator it = downloads_observed_.begin();
77 it != downloads_observed_.end(); ++it)
78 (*it)->RemoveObserver(this);
80 if (download_manager_)
81 download_manager_->RemoveObserver(this);
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);
92 finished_downloads_at_construction_ = finished_downloads_.size();
93 states_observed_.clear();
96 void DownloadTestObserver::ManagerGoingDown(DownloadManager* manager) {
97 CHECK_EQ(manager, download_manager_);
98 download_manager_ = NULL;
99 SignalIfFinished();
102 void DownloadTestObserver::WaitForFinished() {
103 if (!IsFinished()) {
104 waiting_ = true;
105 RunMessageLoop();
106 waiting_ = false;
110 bool DownloadTestObserver::IsFinished() const {
111 return (finished_downloads_.size() - finished_downloads_at_construction_ >=
112 wait_count_) || (download_manager_ == NULL);
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);
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);
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());
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;
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;
168 case ON_DANGEROUS_DOWNLOAD_FAIL:
169 ADD_FAILURE() << "Unexpected dangerous download item.";
170 break;
172 case ON_DANGEROUS_DOWNLOAD_IGNORE:
173 break;
175 case ON_DANGEROUS_DOWNLOAD_QUIT:
176 DownloadInFinalState(download);
177 break;
179 default:
180 NOTREACHED();
184 if (IsDownloadInFinalState(download))
185 DownloadInFinalState(download);
188 size_t DownloadTestObserver::NumDangerousDownloadsSeen() const {
189 return dangerous_downloads_seen_.size();
192 size_t DownloadTestObserver::NumDownloadsSeenInState(
193 DownloadItem::DownloadState state) const {
194 StateMap::const_iterator it = states_observed_.find(state);
196 if (it == states_observed_.end())
197 return 0;
199 return it->second;
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;
208 // Record the transition.
209 finished_downloads_.insert(download);
211 // Record the state.
212 states_observed_[download->GetState()]++; // Initializes to 0 the first time.
214 SignalIfFinished();
217 void DownloadTestObserver::SignalIfFinished() {
218 if (waiting_ && IsFinished())
219 base::MessageLoopForUI::current()->Quit();
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();
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();
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();
256 DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
260 bool DownloadTestObserverTerminal::IsDownloadInFinalState(
261 DownloadItem* download) {
262 return download->IsDone();
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();
278 DownloadTestObserverInProgress::~DownloadTestObserverInProgress() {
282 bool DownloadTestObserverInProgress::IsDownloadInFinalState(
283 DownloadItem* download) {
284 return (download->GetState() == DownloadItem::IN_PROGRESS) &&
285 !download->GetTargetFilePath().empty();
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();
302 DownloadTestObserverInterrupted::~DownloadTestObserverInterrupted() {
306 bool DownloadTestObserverInterrupted::IsDownloadInFinalState(
307 DownloadItem* download) {
308 return download->GetState() == DownloadItem::INTERRUPTED;
311 DownloadTestFlushObserver::DownloadTestFlushObserver(
312 DownloadManager* download_manager)
313 : download_manager_(download_manager),
314 waiting_for_zero_inprogress_(true) {}
316 void DownloadTestFlushObserver::WaitForFlush() {
317 DCHECK_CURRENTLY_ON(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();
325 void DownloadTestFlushObserver::OnDownloadCreated(
326 DownloadManager* manager,
327 DownloadItem* item) {
328 CheckDownloadsInProgress(true);
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);
339 void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) {
340 // No change in DownloadItem set on manager.
341 CheckDownloadsInProgress(false);
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);
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;
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);
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.
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);
386 downloads_observed_.clear();
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));
397 void DownloadTestFlushObserver::PingFileThread(int cycle) {
398 BrowserThread::PostTask(
399 BrowserThread::IO, FROM_HERE,
400 base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle));
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());
414 DownloadTestItemCreationObserver::DownloadTestItemCreationObserver()
415 : download_id_(DownloadItem::kInvalidId),
416 interrupt_reason_(DOWNLOAD_INTERRUPT_REASON_NONE),
417 called_back_count_(0),
418 waiting_(false) {
421 DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
424 void DownloadTestItemCreationObserver::WaitForDownloadItemCreation() {
425 DCHECK_CURRENTLY_ON(BrowserThread::UI);
427 if (called_back_count_ == 0) {
428 waiting_ = true;
429 RunMessageLoop();
430 waiting_ = false;
434 void DownloadTestItemCreationObserver::DownloadItemCreationCallback(
435 DownloadItem* item,
436 DownloadInterruptReason interrupt_reason) {
437 DCHECK_CURRENTLY_ON(BrowserThread::UI);
439 if (item)
440 download_id_ = item->GetId();
441 interrupt_reason_ = interrupt_reason;
442 ++called_back_count_;
443 DCHECK_EQ(1u, called_back_count_);
445 if (waiting_)
446 base::MessageLoopForUI::current()->Quit();
449 const DownloadUrlParameters::OnStartedCallback
450 DownloadTestItemCreationObserver::callback() {
451 return base::Bind(
452 &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this);
455 } // namespace content