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"
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"
21 DownloadUpdatedObserver::DownloadUpdatedObserver(
22 DownloadItem
* item
, DownloadUpdatedObserver::EventFilter filter
)
27 item
->AddObserver(this);
30 DownloadUpdatedObserver::~DownloadUpdatedObserver() {
32 item_
->RemoveObserver(this);
35 bool DownloadUpdatedObserver::WaitForEvent() {
36 if (item_
&& filter_
.Run(item_
))
47 void DownloadUpdatedObserver::OnDownloadUpdated(DownloadItem
* item
) {
48 DCHECK_EQ(item_
, item
);
49 if (filter_
.Run(item_
))
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);
60 base::MessageLoopForUI::current()->Quit();
63 DownloadTestObserver::DownloadTestObserver(
64 DownloadManager
* download_manager
,
66 DangerousDownloadAction dangerous_download_action
)
67 : download_manager_(download_manager
),
68 wait_count_(wait_count
),
69 finished_downloads_at_construction_(0),
71 dangerous_download_action_(dangerous_download_action
),
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
;
102 void DownloadTestObserver::WaitForFinished() {
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
151 BrowserThread::PostTask(
152 BrowserThread::UI
, FROM_HERE
,
153 base::Bind(&DownloadTestObserver::AcceptDangerousDownload
,
154 weak_factory_
.GetWeakPtr(),
158 case ON_DANGEROUS_DOWNLOAD_DENY
:
159 // Fake a user click on "Deny". Delay the actual click, as the
161 BrowserThread::PostTask(
162 BrowserThread::UI
, FROM_HERE
,
163 base::Bind(&DownloadTestObserver::DenyDangerousDownload
,
164 weak_factory_
.GetWeakPtr(),
168 case ON_DANGEROUS_DOWNLOAD_FAIL
:
169 ADD_FAILURE() << "Unexpected dangerous download item.";
172 case ON_DANGEROUS_DOWNLOAD_IGNORE
:
175 case ON_DANGEROUS_DOWNLOAD_QUIT
:
176 DownloadInFinalState(download
);
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())
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.
208 // Record the transition.
209 finished_downloads_
.insert(download
);
212 states_observed_
[download
->GetState()]++; // Initializes to 0 the first time.
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
225 if (!download_manager_
)
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
235 if (!download_manager_
)
237 DownloadItem
* download
= download_manager_
->GetDownload(download_id
);
238 if (download
&& !download
->IsDone())
242 DownloadTestObserverTerminal::DownloadTestObserverTerminal(
243 DownloadManager
* download_manager
,
245 DangerousDownloadAction dangerous_download_action
)
246 : DownloadTestObserver(download_manager
,
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()|.
256 DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
260 bool DownloadTestObserverTerminal::IsDownloadInFinalState(
261 DownloadItem
* download
) {
262 return download
->IsDone();
265 DownloadTestObserverInProgress::DownloadTestObserverInProgress(
266 DownloadManager
* download_manager
,
268 : DownloadTestObserver(download_manager
,
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()|.
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
,
291 DangerousDownloadAction dangerous_download_action
)
292 : DownloadTestObserver(download_manager
,
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()|.
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(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();
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_
) {
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
)
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.
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
) {
405 BrowserThread::PostTask(
406 BrowserThread::UI
, FROM_HERE
,
407 base::Bind(&DownloadTestFlushObserver::PingFileThread
, this, cycle
));
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),
421 DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
424 void DownloadTestItemCreationObserver::WaitForDownloadItemCreation() {
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
427 if (called_back_count_
== 0) {
434 void DownloadTestItemCreationObserver::DownloadItemCreationCallback(
436 DownloadInterruptReason interrupt_reason
) {
437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
440 download_id_
= item
->GetId();
441 interrupt_reason_
= interrupt_reason
;
442 ++called_back_count_
;
443 DCHECK_EQ(1u, called_back_count_
);
446 base::MessageLoopForUI::current()->Quit();
449 const DownloadUrlParameters::OnStartedCallback
450 DownloadTestItemCreationObserver::callback() {
452 &DownloadTestItemCreationObserver::DownloadItemCreationCallback
, this);
455 } // namespace content