• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DownloadUpdatedObserver(DownloadItem * item,DownloadUpdatedObserver::EventFilter filter)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 
~DownloadUpdatedObserver()30 DownloadUpdatedObserver::~DownloadUpdatedObserver() {
31   if (item_)
32     item_->RemoveObserver(this);
33 }
34 
WaitForEvent()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 
OnDownloadUpdated(DownloadItem * item)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 
OnDownloadDestroyed(DownloadItem * item)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 
DownloadTestObserver(DownloadManager * download_manager,size_t wait_count,DangerousDownloadAction dangerous_download_action)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 
~DownloadTestObserver()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 
Init()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 
ManagerGoingDown(DownloadManager * manager)96 void DownloadTestObserver::ManagerGoingDown(DownloadManager* manager) {
97   CHECK_EQ(manager, download_manager_);
98   download_manager_ = NULL;
99   SignalIfFinished();
100 }
101 
WaitForFinished()102 void DownloadTestObserver::WaitForFinished() {
103   if (!IsFinished()) {
104     waiting_ = true;
105     RunMessageLoop();
106     waiting_ = false;
107   }
108 }
109 
IsFinished() const110 bool DownloadTestObserver::IsFinished() const {
111   return (finished_downloads_.size() - finished_downloads_at_construction_ >=
112           wait_count_) || (download_manager_ == NULL);
113 }
114 
OnDownloadCreated(DownloadManager * manager,DownloadItem * item)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 
OnDownloadDestroyed(DownloadItem * download)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 
OnDownloadUpdated(DownloadItem * download)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 
NumDangerousDownloadsSeen() const188 size_t DownloadTestObserver::NumDangerousDownloadsSeen() const {
189   return dangerous_downloads_seen_.size();
190 }
191 
NumDownloadsSeenInState(DownloadItem::DownloadState state) const192 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 
DownloadInFinalState(DownloadItem * download)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 
SignalIfFinished()217 void DownloadTestObserver::SignalIfFinished() {
218   if (waiting_ && IsFinished())
219     base::MessageLoopForUI::current()->Quit();
220 }
221 
AcceptDangerousDownload(uint32 download_id)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 
DenyDangerousDownload(uint32 download_id)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 
DownloadTestObserverTerminal(DownloadManager * download_manager,size_t wait_count,DangerousDownloadAction dangerous_download_action)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 
~DownloadTestObserverTerminal()256 DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
257 }
258 
259 
IsDownloadInFinalState(DownloadItem * download)260 bool DownloadTestObserverTerminal::IsDownloadInFinalState(
261     DownloadItem* download) {
262   return download->IsDone();
263 }
264 
DownloadTestObserverInProgress(DownloadManager * download_manager,size_t wait_count)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 
~DownloadTestObserverInProgress()278 DownloadTestObserverInProgress::~DownloadTestObserverInProgress() {
279 }
280 
281 
IsDownloadInFinalState(DownloadItem * download)282 bool DownloadTestObserverInProgress::IsDownloadInFinalState(
283     DownloadItem* download) {
284   return (download->GetState() == DownloadItem::IN_PROGRESS) &&
285       !download->GetTargetFilePath().empty();
286 }
287 
DownloadTestObserverInterrupted(DownloadManager * download_manager,size_t wait_count,DangerousDownloadAction dangerous_download_action)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 
~DownloadTestObserverInterrupted()302 DownloadTestObserverInterrupted::~DownloadTestObserverInterrupted() {
303 }
304 
305 
IsDownloadInFinalState(DownloadItem * download)306 bool DownloadTestObserverInterrupted::IsDownloadInFinalState(
307     DownloadItem* download) {
308   return download->GetState() == DownloadItem::INTERRUPTED;
309 }
310 
DownloadTestFlushObserver(DownloadManager * download_manager)311 DownloadTestFlushObserver::DownloadTestFlushObserver(
312     DownloadManager* download_manager)
313     : download_manager_(download_manager),
314       waiting_for_zero_inprogress_(true) {}
315 
WaitForFlush()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 
OnDownloadCreated(DownloadManager * manager,DownloadItem * item)325 void DownloadTestFlushObserver::OnDownloadCreated(
326     DownloadManager* manager,
327     DownloadItem* item) {
328   CheckDownloadsInProgress(true);
329 }
330 
OnDownloadDestroyed(DownloadItem * download)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 
OnDownloadUpdated(DownloadItem * download)339 void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) {
340   // No change in DownloadItem set on manager.
341   CheckDownloadsInProgress(false);
342 }
343 
~DownloadTestFlushObserver()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.
CheckDownloadsInProgress(bool observe_downloads)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 
PingFileThread(int cycle)397 void DownloadTestFlushObserver::PingFileThread(int cycle) {
398   BrowserThread::PostTask(
399       BrowserThread::IO, FROM_HERE,
400       base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle));
401 }
402 
PingIOThread(int 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());
411   }
412 }
413 
DownloadTestItemCreationObserver()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 
~DownloadTestItemCreationObserver()421 DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
422 }
423 
WaitForDownloadItemCreation()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 
DownloadItemCreationCallback(DownloadItem * item,DownloadInterruptReason interrupt_reason)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
callback()450     DownloadTestItemCreationObserver::callback() {
451   return base::Bind(
452       &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this);
453 }
454 
455 }  // namespace content
456