download_test_observer.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 "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      default:
176        NOTREACHED();
177    }
178  }
179
180  if (IsDownloadInFinalState(download))
181    DownloadInFinalState(download);
182}
183
184size_t DownloadTestObserver::NumDangerousDownloadsSeen() const {
185  return dangerous_downloads_seen_.size();
186}
187
188size_t DownloadTestObserver::NumDownloadsSeenInState(
189    DownloadItem::DownloadState state) const {
190  StateMap::const_iterator it = states_observed_.find(state);
191
192  if (it == states_observed_.end())
193    return 0;
194
195  return it->second;
196}
197
198void DownloadTestObserver::DownloadInFinalState(DownloadItem* download) {
199  if (finished_downloads_.find(download) != finished_downloads_.end()) {
200    // We've already seen the final state on this download.
201    return;
202  }
203
204  // Record the transition.
205  finished_downloads_.insert(download);
206
207  // Record the state.
208  states_observed_[download->GetState()]++;  // Initializes to 0 the first time.
209
210  SignalIfFinished();
211}
212
213void DownloadTestObserver::SignalIfFinished() {
214  if (waiting_ && IsFinished())
215    base::MessageLoopForUI::current()->Quit();
216}
217
218void DownloadTestObserver::AcceptDangerousDownload(int32 download_id) {
219  // Download manager was shutdown before the UI thread could accept the
220  // download.
221  if (!download_manager_)
222    return;
223  DownloadItem* download = download_manager_->GetDownload(download_id);
224  if (download && !download->IsDone())
225    download->ValidateDangerousDownload();
226}
227
228void DownloadTestObserver::DenyDangerousDownload(int32 download_id) {
229  // Download manager was shutdown before the UI thread could deny the
230  // download.
231  if (!download_manager_)
232    return;
233  DownloadItem* download = download_manager_->GetDownload(download_id);
234  if (download && !download->IsDone())
235    download->Remove();
236}
237
238DownloadTestObserverTerminal::DownloadTestObserverTerminal(
239    DownloadManager* download_manager,
240    size_t wait_count,
241    DangerousDownloadAction dangerous_download_action)
242        : DownloadTestObserver(download_manager,
243                               wait_count,
244                               dangerous_download_action) {
245  // You can't rely on overriden virtual functions in a base class constructor;
246  // the virtual function table hasn't been set up yet.  So, we have to do any
247  // work that depends on those functions in the derived class constructor
248  // instead.  In this case, it's because of |IsDownloadInFinalState()|.
249  Init();
250}
251
252DownloadTestObserverTerminal::~DownloadTestObserverTerminal() {
253}
254
255
256bool DownloadTestObserverTerminal::IsDownloadInFinalState(
257    DownloadItem* download) {
258  return (download->GetState() != DownloadItem::IN_PROGRESS);
259}
260
261DownloadTestObserverInProgress::DownloadTestObserverInProgress(
262    DownloadManager* download_manager,
263    size_t wait_count)
264        : DownloadTestObserver(download_manager,
265                               wait_count,
266                               ON_DANGEROUS_DOWNLOAD_ACCEPT) {
267  // You can't override virtual functions in a base class constructor; the
268  // virtual function table hasn't been set up yet.  So, we have to do any
269  // work that depends on those functions in the derived class constructor
270  // instead.  In this case, it's because of |IsDownloadInFinalState()|.
271  Init();
272}
273
274DownloadTestObserverInProgress::~DownloadTestObserverInProgress() {
275}
276
277
278bool DownloadTestObserverInProgress::IsDownloadInFinalState(
279    DownloadItem* download) {
280  return (download->GetState() == DownloadItem::IN_PROGRESS) &&
281      !download->GetTargetFilePath().empty();
282}
283
284DownloadTestFlushObserver::DownloadTestFlushObserver(
285    DownloadManager* download_manager)
286    : download_manager_(download_manager),
287      waiting_for_zero_inprogress_(true) {}
288
289void DownloadTestFlushObserver::WaitForFlush() {
290  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291  download_manager_->AddObserver(this);
292  // The wait condition may have been met before WaitForFlush() was called.
293  CheckDownloadsInProgress(true);
294  BrowserThread::GetBlockingPool()->FlushForTesting();
295  RunMessageLoop();
296}
297
298void DownloadTestFlushObserver::OnDownloadCreated(
299    DownloadManager* manager,
300    DownloadItem* item) {
301  CheckDownloadsInProgress(true);
302}
303
304void DownloadTestFlushObserver::OnDownloadDestroyed(DownloadItem* download) {
305  // Stop observing.  Do not do anything with it, as it is about to be gone.
306  DownloadSet::iterator it = downloads_observed_.find(download);
307  ASSERT_TRUE(it != downloads_observed_.end());
308  downloads_observed_.erase(it);
309  download->RemoveObserver(this);
310}
311
312void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) {
313  // No change in DownloadItem set on manager.
314  CheckDownloadsInProgress(false);
315}
316
317DownloadTestFlushObserver::~DownloadTestFlushObserver() {
318  download_manager_->RemoveObserver(this);
319  for (DownloadSet::iterator it = downloads_observed_.begin();
320       it != downloads_observed_.end(); ++it) {
321    (*it)->RemoveObserver(this);
322  }
323}
324
325// If we're waiting for that flush point, check the number
326// of downloads in the IN_PROGRESS state and take appropriate
327// action.  If requested, also observes all downloads while iterating.
328void DownloadTestFlushObserver::CheckDownloadsInProgress(
329    bool observe_downloads) {
330  if (waiting_for_zero_inprogress_) {
331    int count = 0;
332
333    std::vector<DownloadItem*> downloads;
334    download_manager_->GetAllDownloads(&downloads);
335    for (std::vector<DownloadItem*>::iterator it = downloads.begin();
336         it != downloads.end(); ++it) {
337      if ((*it)->GetState() == DownloadItem::IN_PROGRESS)
338        count++;
339      if (observe_downloads) {
340        if (downloads_observed_.find(*it) == downloads_observed_.end()) {
341          (*it)->AddObserver(this);
342          downloads_observed_.insert(*it);
343        }
344        // Download items are forever, and we don't want to make
345        // assumptions about future state transitions, so once we
346        // start observing them, we don't stop until destruction.
347      }
348    }
349
350    if (count == 0) {
351      waiting_for_zero_inprogress_ = false;
352      // Stop observing DownloadItems.  We maintain the observation
353      // of DownloadManager so that we don't have to independently track
354      // whether we are observing it for conditional destruction.
355      for (DownloadSet::iterator it = downloads_observed_.begin();
356           it != downloads_observed_.end(); ++it) {
357        (*it)->RemoveObserver(this);
358      }
359      downloads_observed_.clear();
360
361      // Trigger next step.  We need to go past the IO thread twice, as
362      // there's a self-task posting in the IO thread cancel path.
363      BrowserThread::PostTask(
364          BrowserThread::FILE, FROM_HERE,
365          base::Bind(&DownloadTestFlushObserver::PingFileThread, this, 2));
366    }
367  }
368}
369
370void DownloadTestFlushObserver::PingFileThread(int cycle) {
371  BrowserThread::PostTask(
372      BrowserThread::IO, FROM_HERE,
373      base::Bind(&DownloadTestFlushObserver::PingIOThread, this, cycle));
374}
375
376void DownloadTestFlushObserver::PingIOThread(int cycle) {
377  if (--cycle) {
378    BrowserThread::PostTask(
379        BrowserThread::UI, FROM_HERE,
380        base::Bind(&DownloadTestFlushObserver::PingFileThread, this, cycle));
381  } else {
382    BrowserThread::PostTask(
383        BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
384  }
385}
386
387DownloadTestItemCreationObserver::DownloadTestItemCreationObserver()
388    : download_id_(DownloadId::Invalid().local()),
389      error_(net::OK),
390      called_back_count_(0),
391      waiting_(false) {
392}
393
394DownloadTestItemCreationObserver::~DownloadTestItemCreationObserver() {
395}
396
397void DownloadTestItemCreationObserver::WaitForDownloadItemCreation() {
398  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399
400  if (called_back_count_ == 0) {
401    waiting_ = true;
402    RunMessageLoop();
403    waiting_ = false;
404  }
405}
406
407void DownloadTestItemCreationObserver::DownloadItemCreationCallback(
408    DownloadItem* item,
409    net::Error error) {
410  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411
412  if (item)
413    download_id_ = item->GetId();
414  error_ = error;
415  ++called_back_count_;
416  DCHECK_EQ(1u, called_back_count_);
417
418  if (waiting_)
419    base::MessageLoopForUI::current()->Quit();
420}
421
422const DownloadUrlParameters::OnStartedCallback
423    DownloadTestItemCreationObserver::callback() {
424  return base::Bind(
425      &DownloadTestItemCreationObserver::DownloadItemCreationCallback, this);
426}
427
428}  // namespace content
429