download_test_observer.h revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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#ifndef CONTENT_TEST_DOWNLOAD_TEST_OBSERVER_H_
6#define CONTENT_TEST_DOWNLOAD_TEST_OBSERVER_H_
7
8#include <set>
9#include <vector>
10
11#include "base/basictypes.h"
12#include "base/callback_forward.h"
13#include "base/memory/ref_counted.h"
14#include "content/public/browser/download_item.h"
15#include "content/public/browser/download_manager.h"
16#include "content/public/browser/download_url_parameters.h"
17#include "net/base/net_errors.h"
18
19namespace content {
20
21// Detects an arbitrary change on a download item.
22// TODO: Rewrite other observers to use this (or be replaced by it).
23class DownloadUpdatedObserver : public DownloadItem::Observer {
24 public:
25  typedef base::Callback<bool(DownloadItem*)> EventFilter;
26
27  // The filter passed may be called multiple times, even after it
28  // returns true.
29  DownloadUpdatedObserver(DownloadItem* item, EventFilter filter);
30  virtual ~DownloadUpdatedObserver();
31
32  // Returns when either the event has been seen (at least once since
33  // object construction) or the item is destroyed.  Return value indicates
34  // if the wait ended because the item was seen (true) or the object
35  // destroyed (false).
36  bool WaitForEvent();
37
38 private:
39  // DownloadItem::Observer
40  virtual void OnDownloadUpdated(DownloadItem* item) OVERRIDE;
41  virtual void OnDownloadDestroyed(DownloadItem* item) OVERRIDE;
42
43  DownloadItem* item_;
44  EventFilter filter_;
45  bool waiting_;
46  bool event_seen_;
47
48  DISALLOW_COPY_AND_ASSIGN(DownloadUpdatedObserver);
49};
50
51// Detects changes to the downloads after construction.
52// Finishes when one of the following happens:
53//   - A specified number of downloads change to a terminal state (defined
54//     in derived classes).
55//   - Specific events, such as a select file dialog.
56// Callers may either probe for the finished state, or wait on it.
57//
58// TODO(rdsmith): Detect manager going down, remove pointer to
59// DownloadManager, transition to finished.  (For right now we
60// just use a scoped_refptr<> to keep it around, but that may cause
61// timeouts on waiting if a DownloadManager::Shutdown() occurs which
62// cancels our in-progress downloads.)
63class DownloadTestObserver : public DownloadManager::Observer,
64                             public DownloadItem::Observer {
65 public:
66  // Action an observer should take if a dangerous download is encountered.
67  enum DangerousDownloadAction {
68    ON_DANGEROUS_DOWNLOAD_ACCEPT,  // Accept the download
69    ON_DANGEROUS_DOWNLOAD_DENY,    // Deny the download
70    ON_DANGEROUS_DOWNLOAD_FAIL,    // Fail if a dangerous download is seen
71    ON_DANGEROUS_DOWNLOAD_IGNORE   // Make it the callers problem.
72  };
73
74  // Create an object that will be considered finished when |wait_count|
75  // download items have entered a terminal state.
76  DownloadTestObserver(DownloadManager* download_manager,
77                       size_t wait_count,
78                       DangerousDownloadAction dangerous_download_action);
79
80  virtual ~DownloadTestObserver();
81
82  // Wait for the requested number of downloads to enter a terminal state.
83  void WaitForFinished();
84
85  // Return true if everything's happened that we're configured for.
86  bool IsFinished() const;
87
88  // DownloadItem::Observer
89  virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE;
90  virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE;
91
92  // DownloadManager::Observer
93  virtual void OnDownloadCreated(
94      DownloadManager* manager, DownloadItem* item) OVERRIDE;
95
96  size_t NumDangerousDownloadsSeen() const;
97
98  size_t NumDownloadsSeenInState(DownloadItem::DownloadState state) const;
99
100 protected:
101  // Only to be called by derived classes' constructors.
102  virtual void Init();
103
104  // Called to see if a download item is in a final state.
105  virtual bool IsDownloadInFinalState(DownloadItem* download) = 0;
106
107 private:
108  typedef std::set<DownloadItem*> DownloadSet;
109
110  // Maps states to the number of times they have been encountered
111  typedef std::map<DownloadItem::DownloadState, size_t> StateMap;
112
113  // Called when we know that a download item is in a final state.
114  // Note that this is not the same as it first transitioning in to the
115  // final state; multiple notifications may occur once the item is in
116  // that state.  So we keep our own track of transitions into final.
117  void DownloadInFinalState(DownloadItem* download);
118
119  void SignalIfFinished();
120
121  // The observed download manager.
122  scoped_refptr<DownloadManager> download_manager_;
123
124  // The set of DownloadItem's that have transitioned to their finished state
125  // since construction of this object.  When the size of this array
126  // reaches wait_count_, we're done.
127  DownloadSet finished_downloads_;
128
129  // The set of DownloadItem's we are currently observing.  Generally there
130  // won't be any overlap with the above; once we see the final state
131  // on a DownloadItem, we'll stop observing it.
132  DownloadSet downloads_observed_;
133
134  // The map of states to the number of times they have been observed since
135  // we started looking.
136  // Recorded at the time downloads_observed_ is recorded, but cleared in the
137  // constructor to exclude pre-existing states.
138  StateMap states_observed_;
139
140  // The number of downloads to wait on completing.
141  size_t wait_count_;
142
143  // The number of downloads entered in final state in Init().  We use
144  // |finished_downloads_| to track the incoming transitions to final state we
145  // should ignore, and to track the number of final state transitions that
146  // occurred between construction and return from wait.  But some downloads may
147  // be in our final state (and thus be entered into |finished_downloads_|) when
148  // we construct this class.  We don't want to count those in our transition to
149  // finished.
150  int finished_downloads_at_construction_;
151
152  // Whether an internal message loop has been started and must be quit upon
153  // all downloads completing.
154  bool waiting_;
155
156  // Action to take if a dangerous download is encountered.
157  DangerousDownloadAction dangerous_download_action_;
158
159  // Holds the download ids which were dangerous.
160  std::set<int32> dangerous_downloads_seen_;
161
162  DISALLOW_COPY_AND_ASSIGN(DownloadTestObserver);
163};
164
165class DownloadTestObserverTerminal : public DownloadTestObserver {
166 public:
167  // Create an object that will be considered finished when |wait_count|
168  // download items have entered a terminal state (any but IN_PROGRESS).
169  // If |finish_on_select_file| is true, the object will also be
170  // considered finished if the DownloadManager raises a
171  // SelectFileDialogDisplayed() notification.
172  DownloadTestObserverTerminal(
173      DownloadManager* download_manager,
174      size_t wait_count,
175      DangerousDownloadAction dangerous_download_action);
176
177  virtual ~DownloadTestObserverTerminal();
178
179 private:
180  virtual bool IsDownloadInFinalState(DownloadItem* download) OVERRIDE;
181
182  DISALLOW_COPY_AND_ASSIGN(DownloadTestObserverTerminal);
183};
184
185// Detects changes to the downloads after construction.
186// Finishes when a specified number of downloads change to the
187// IN_PROGRESS state, or a Select File Dialog has appeared.
188// Dangerous downloads are accepted.
189// Callers may either probe for the finished state, or wait on it.
190class DownloadTestObserverInProgress : public DownloadTestObserver {
191 public:
192  // Create an object that will be considered finished when |wait_count|
193  // download items have entered state |IN_PROGRESS|.
194  // If |finish_on_select_file| is true, the object will also be
195  // considered finished if the DownloadManager raises a
196  // SelectFileDialogDisplayed() notification.
197  DownloadTestObserverInProgress(
198      DownloadManager* download_manager, size_t wait_count);
199
200  virtual ~DownloadTestObserverInProgress();
201
202 private:
203  virtual bool IsDownloadInFinalState(DownloadItem* download) OVERRIDE;
204
205  DISALLOW_COPY_AND_ASSIGN(DownloadTestObserverInProgress);
206};
207
208// The WaitForFlush() method on this class returns after:
209//      * There are no IN_PROGRESS download items remaining on the
210//        DownloadManager.
211//      * There have been two round trip messages through the file and
212//        IO threads.
213// This almost certainly means that a Download cancel has propagated through
214// the system.
215class DownloadTestFlushObserver
216    : public DownloadManager::Observer,
217      public DownloadItem::Observer,
218      public base::RefCountedThreadSafe<DownloadTestFlushObserver> {
219 public:
220  explicit DownloadTestFlushObserver(DownloadManager* download_manager);
221
222  void WaitForFlush();
223
224  // DownloadsManager observer methods.
225  virtual void OnDownloadCreated(
226      DownloadManager* manager,
227      DownloadItem* item) OVERRIDE;
228
229  // DownloadItem observer methods.
230  virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE;
231  virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE;
232
233 protected:
234  friend class base::RefCountedThreadSafe<DownloadTestFlushObserver>;
235
236  virtual ~DownloadTestFlushObserver();
237
238 private:
239  typedef std::set<DownloadItem*> DownloadSet;
240
241  // If we're waiting for that flush point, check the number
242  // of downloads in the IN_PROGRESS state and take appropriate
243  // action.  If requested, also observes all downloads while iterating.
244  void CheckDownloadsInProgress(bool observe_downloads);
245
246  void PingFileThread(int cycle);
247
248  void PingIOThread(int cycle);
249
250  DownloadManager* download_manager_;
251  DownloadSet downloads_observed_;
252  bool waiting_for_zero_inprogress_;
253
254  DISALLOW_COPY_AND_ASSIGN(DownloadTestFlushObserver);
255};
256
257// Waits for a callback indicating that the DownloadItem is about to be created,
258// or that an error occurred and it won't be created.
259class DownloadTestItemCreationObserver
260    : public base::RefCountedThreadSafe<DownloadTestItemCreationObserver> {
261 public:
262  DownloadTestItemCreationObserver();
263
264  void WaitForDownloadItemCreation();
265
266  int download_id() const { return download_id_; }
267  net::Error error() const { return error_; }
268  bool started() const { return called_back_count_ > 0; }
269  bool succeeded() const { return started() && (error_ == net::OK); }
270
271  const DownloadUrlParameters::OnStartedCallback callback();
272
273 private:
274  friend class base::RefCountedThreadSafe<DownloadTestItemCreationObserver>;
275
276  ~DownloadTestItemCreationObserver();
277
278  void DownloadItemCreationCallback(DownloadItem* item, net::Error error);
279
280  // The download creation information we received.
281  int download_id_;
282  net::Error error_;
283
284  // Count of callbacks.
285  size_t called_back_count_;
286
287  // We are in the message loop.
288  bool waiting_;
289
290  DISALLOW_COPY_AND_ASSIGN(DownloadTestItemCreationObserver);
291};
292
293}  // namespace content`
294
295#endif  // CONTENT_TEST_DOWNLOAD_TEST_OBSERVER_H_
296