download_test_observer.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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
19namespace content {
20
21DownloadUpdatedObserver::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
30DownloadUpdatedObserver::~DownloadUpdatedObserver() {
31  if (item_)
32    item_->RemoveObserver(this);
33}
34
35bool 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
47void 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
55void 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
63DownloadTestObserver::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
75DownloadTestObserver::~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
84void 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
96void DownloadTestObserver::ManagerGoingDown(DownloadManager* manager) {
97  CHECK_EQ(manager, download_manager_);
98  download_manager_ = NULL;
99  SignalIfFinished();
100}
101
102void DownloadTestObserver::WaitForFinished() {
103  if (!IsFinished()) {
104    waiting_ = true;
105    RunMessageLoop();
106    waiting_ = false;
107  }
108}
109
110bool DownloadTestObserver::IsFinished() const {
111  return (finished_downloads_.size() - finished_downloads_at_construction_ >=
112          wait_count_) || (download_manager_ == NULL);
113}
114
115void 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
130void 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
138void 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
188size_t DownloadTestObserver::NumDangerousDownloadsSeen() const {
189  return dangerous_downloads_seen_.size();
190}
191
192size_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
202void 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
217void DownloadTestObserver::SignalIfFinished() {
218  if (waiting_ && IsFinished())
219    base::MessageLoopForUI::current()->Quit();
220}
221
222void 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
232void 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
242DownloadTestObserverTerminal::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
256DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
257}
258
259
260bool DownloadTestObserverTerminal::IsDownloadInFinalState(
261    DownloadItem* download) {
262  return download->IsDone();
263}
264
265DownloadTestObserverInProgress::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
278DownloadTestObserverInProgress::~DownloadTestObserverInProgress() {
279}
280
281
282bool DownloadTestObserverInProgress::IsDownloadInFinalState(
283    DownloadItem* download) {
284  return (download->GetState() == DownloadItem::IN_PROGRESS) &&
285      !download->GetTargetFilePath().empty();
286}
287
288DownloadTestObserverInterrupted::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
302DownloadTestObserverInterrupted::~DownloadTestObserverInterrupted() {
303}
304
305
306bool DownloadTestObserverInterrupted::IsDownloadInFinalState(
307    DownloadItem* download) {
308  return download->GetState() == DownloadItem::INTERRUPTED;
309}
310
311DownloadTestFlushObserver::DownloadTestFlushObserver(
312    DownloadManager* download_manager)
313    : download_manager_(download_manager),
314      waiting_for_zero_inprogress_(true) {}
315
316void 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
325void DownloadTestFlushObserver::OnDownloadCreated(
326    DownloadManager* manager,
327    DownloadItem* item) {
328  CheckDownloadsInProgress(true);
329}
330
331void 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
339void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) {
340  // No change in DownloadItem set on manager.
341  CheckDownloadsInProgress(false);
342}
343
344DownloadTestFlushObserver::~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.
355void 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
397void DownloadTestFlushObserver::PingFileThread(int cycle) {
398  BrowserThread::PostTask(
399      BrowserThread::IO, FROM_HERE,
400      base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle));
401}
402
403void 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
414DownloadTestItemCreationObserver::DownloadTestItemCreationObserver()
415    : download_id_(DownloadItem::kInvalidId),
416      error_(net::OK),
417      called_back_count_(0),
418      waiting_(false) {
419}
420
421DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
422}
423
424void 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
434void DownloadTestItemCreationObserver::DownloadItemCreationCallback(
435    DownloadItem* item,
436    net::Error error) {
437  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
438
439  if (item)
440    download_id_ = item->GetId();
441  error_ = error;
442  ++called_back_count_;
443  DCHECK_EQ(1u, called_back_count_);
444
445  if (waiting_)
446    base::MessageLoopForUI::current()->Quit();
447}
448
449const DownloadUrlParameters::OnStartedCallback
450    DownloadTestItemCreationObserver::callback() {
451  return base::Bind(
452      &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this);
453}
454
455}  // namespace content
456