• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "base/bind.h"
6 #include "base/callback.h"
7 #include "base/files/file_path.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/observer_list.h"
12 #include "chrome/browser/download/download_history.h"
13 #include "chrome/browser/download/download_service.h"
14 #include "chrome/browser/download/download_service_factory.h"
15 #include "chrome/browser/download/download_ui_controller.h"
16 #include "chrome/browser/history/download_row.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
19 #include "content/public/test/mock_download_item.h"
20 #include "content/public/test/mock_download_manager.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 using content::MockDownloadItem;
25 using content::MockDownloadManager;
26 using testing::AnyNumber;
27 using testing::Assign;
28 using testing::Return;
29 using testing::ReturnRefOfCopy;
30 using testing::SaveArg;
31 using testing::_;
32 
33 namespace {
34 
35 // A DownloadUIController::Delegate that stores the DownloadItem* for the last
36 // download that was sent to the UI.
37 class TestDelegate : public DownloadUIController::Delegate {
38  public:
39   explicit TestDelegate(base::WeakPtr<content::DownloadItem*> receiver);
~TestDelegate()40   virtual ~TestDelegate() {}
41 
42  private:
43   virtual void OnNewDownloadReady(content::DownloadItem* item) OVERRIDE;
44 
45   base::WeakPtr<content::DownloadItem*> receiver_;
46 };
47 
TestDelegate(base::WeakPtr<content::DownloadItem * > receiver)48 TestDelegate::TestDelegate(base::WeakPtr<content::DownloadItem*> receiver)
49     : receiver_(receiver) {
50 }
51 
OnNewDownloadReady(content::DownloadItem * item)52 void TestDelegate::OnNewDownloadReady(content::DownloadItem* item) {
53   if (receiver_.get())
54     *receiver_ = item;
55 }
56 
57 // A DownloadService that returns a custom DownloadHistory.
58 class TestDownloadService : public DownloadService {
59  public:
60   explicit TestDownloadService(Profile* profile);
61   virtual ~TestDownloadService();
62 
set_download_history(scoped_ptr<DownloadHistory> download_history)63   void set_download_history(scoped_ptr<DownloadHistory> download_history) {
64     download_history_.swap(download_history);
65   }
66   virtual DownloadHistory* GetDownloadHistory() OVERRIDE;
67 
68  private:
69   scoped_ptr<DownloadHistory> download_history_;
70 };
71 
TestDownloadService(Profile * profile)72 TestDownloadService::TestDownloadService(Profile* profile)
73     : DownloadService(profile) {
74 }
75 
~TestDownloadService()76 TestDownloadService::~TestDownloadService() {
77 }
78 
GetDownloadHistory()79 DownloadHistory* TestDownloadService::GetDownloadHistory() {
80   return download_history_.get();
81 }
82 
83 // The test fixture:
84 class DownloadUIControllerTest : public ChromeRenderViewHostTestHarness {
85  public:
86   DownloadUIControllerTest();
87 
88  protected:
89   // testing::Test
90   virtual void SetUp() OVERRIDE;
91 
92   // Returns a TestDelegate. Invoking OnNewDownloadReady on the returned
93   // delegate results in the DownloadItem* being stored in |notified_item_|.
94   scoped_ptr<DownloadUIController::Delegate> GetTestDelegate();
95 
manager()96   MockDownloadManager* manager() { return manager_.get(); }
97 
98   // Returns the DownloadManager::Observer registered by a test case. This is
99   // the DownloadUIController's observer for all current test cases.
manager_observer()100   content::DownloadManager::Observer* manager_observer() {
101     return manager_observer_;
102   }
103 
104   // The most recent DownloadItem that was passed into OnNewDownloadReady().
notified_item()105   content::DownloadItem* notified_item() { return notified_item_; }
106 
107   // DownloadHistory performs a query of existing downloads when it is first
108   // instantiated. This method returns the completion callback for that query.
109   // It can be used to inject history downloads.
history_query_callback() const110   const HistoryService::DownloadQueryCallback& history_query_callback() const {
111     return history_adapter_->download_query_callback_;
112   }
113 
114   // DownloadManager::Observer registered by DownloadHistory.
download_history_manager_observer()115   content::DownloadManager::Observer* download_history_manager_observer() {
116     return download_history_manager_observer_;
117   }
118 
119   scoped_ptr<MockDownloadItem> CreateMockInProgressDownload();
120 
121  private:
122   // A private history adapter that stores the DownloadQueryCallback when
123   // QueryDownloads is called.
124   class HistoryAdapter : public DownloadHistory::HistoryAdapter {
125    public:
HistoryAdapter()126     HistoryAdapter() : DownloadHistory::HistoryAdapter(NULL) {}
127     HistoryService::DownloadQueryCallback download_query_callback_;
128 
129    private:
QueryDownloads(const HistoryService::DownloadQueryCallback & callback)130     virtual void QueryDownloads(
131         const HistoryService::DownloadQueryCallback& callback) OVERRIDE {
132       download_query_callback_ = callback;
133     }
134   };
135 
136   // Constructs and returns a TestDownloadService.
137   static KeyedService* TestingDownloadServiceFactory(
138       content::BrowserContext* browser_context);
139 
140   scoped_ptr<MockDownloadManager> manager_;
141   content::DownloadManager::Observer* download_history_manager_observer_;
142   content::DownloadManager::Observer* manager_observer_;
143   content::DownloadItem* notified_item_;
144   base::WeakPtrFactory<content::DownloadItem*> notified_item_receiver_factory_;
145 
146   HistoryAdapter* history_adapter_;
147 };
148 
149 // static
TestingDownloadServiceFactory(content::BrowserContext * browser_context)150 KeyedService* DownloadUIControllerTest::TestingDownloadServiceFactory(
151     content::BrowserContext* browser_context) {
152   return new TestDownloadService(Profile::FromBrowserContext(browser_context));
153 }
154 
DownloadUIControllerTest()155 DownloadUIControllerTest::DownloadUIControllerTest()
156     : download_history_manager_observer_(NULL),
157       manager_observer_(NULL),
158       notified_item_(NULL),
159       notified_item_receiver_factory_(&notified_item_) {
160 }
161 
SetUp()162 void DownloadUIControllerTest::SetUp() {
163   ChromeRenderViewHostTestHarness::SetUp();
164 
165   manager_.reset(new testing::StrictMock<MockDownloadManager>());
166   EXPECT_CALL(*manager_, AddObserver(_))
167       .WillOnce(SaveArg<0>(&download_history_manager_observer_));
168   EXPECT_CALL(*manager_,
169               RemoveObserver(testing::Eq(
170                   testing::ByRef(download_history_manager_observer_))))
171       .WillOnce(testing::Assign(
172           &download_history_manager_observer_,
173           static_cast<content::DownloadManager::Observer*>(NULL)));
174   EXPECT_CALL(*manager_, GetAllDownloads(_)).Times(AnyNumber());
175 
176   scoped_ptr<HistoryAdapter> history_adapter(new HistoryAdapter);
177   history_adapter_ = history_adapter.get();
178   scoped_ptr<DownloadHistory> download_history(new DownloadHistory(
179       manager_.get(),
180       history_adapter.PassAs<DownloadHistory::HistoryAdapter>()));
181   ASSERT_TRUE(download_history_manager_observer_);
182 
183   EXPECT_CALL(*manager_, AddObserver(_))
184       .WillOnce(SaveArg<0>(&manager_observer_));
185   EXPECT_CALL(*manager_,
186               RemoveObserver(testing::Eq(testing::ByRef(manager_observer_))))
187       .WillOnce(testing::Assign(
188           &manager_observer_,
189           static_cast<content::DownloadManager::Observer*>(NULL)));
190   TestDownloadService* download_service = static_cast<TestDownloadService*>(
191       DownloadServiceFactory::GetInstance()->SetTestingFactoryAndUse(
192           browser_context(), &TestingDownloadServiceFactory));
193   ASSERT_TRUE(download_service);
194   download_service->set_download_history(download_history.Pass());
195 }
196 
197 scoped_ptr<MockDownloadItem>
CreateMockInProgressDownload()198 DownloadUIControllerTest::CreateMockInProgressDownload() {
199   scoped_ptr<MockDownloadItem> item(
200       new testing::StrictMock<MockDownloadItem>());
201   EXPECT_CALL(*item, GetBrowserContext())
202       .WillRepeatedly(Return(browser_context()));
203   EXPECT_CALL(*item, GetId()).WillRepeatedly(Return(1));
204   EXPECT_CALL(*item, GetTargetFilePath()).WillRepeatedly(
205       ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
206   EXPECT_CALL(*item, GetFullPath()).WillRepeatedly(
207       ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
208   EXPECT_CALL(*item, GetState())
209       .WillRepeatedly(Return(content::DownloadItem::IN_PROGRESS));
210   EXPECT_CALL(*item, GetUrlChain())
211       .WillRepeatedly(testing::ReturnRefOfCopy(std::vector<GURL>()));
212   EXPECT_CALL(*item, GetReferrerUrl())
213       .WillRepeatedly(testing::ReturnRefOfCopy(GURL()));
214   EXPECT_CALL(*item, GetStartTime()).WillRepeatedly(Return(base::Time()));
215   EXPECT_CALL(*item, GetEndTime()).WillRepeatedly(Return(base::Time()));
216   EXPECT_CALL(*item, GetETag()).WillRepeatedly(ReturnRefOfCopy(std::string()));
217   EXPECT_CALL(*item, GetLastModifiedTime())
218       .WillRepeatedly(ReturnRefOfCopy(std::string()));
219   EXPECT_CALL(*item, GetDangerType())
220       .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
221   EXPECT_CALL(*item, GetLastReason())
222       .WillRepeatedly(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE));
223   EXPECT_CALL(*item, GetReceivedBytes()).WillRepeatedly(Return(0));
224   EXPECT_CALL(*item, GetTotalBytes()).WillRepeatedly(Return(0));
225   EXPECT_CALL(*item, GetTargetDisposition()).WillRepeatedly(
226       Return(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE));
227   EXPECT_CALL(*item, GetOpened()).WillRepeatedly(Return(false));
228   EXPECT_CALL(*item, GetMimeType()).WillRepeatedly(Return(std::string()));
229   EXPECT_CALL(*item, GetURL()).WillRepeatedly(testing::ReturnRefOfCopy(GURL()));
230   EXPECT_CALL(*item, IsTemporary()).WillRepeatedly(Return(false));
231   return item.Pass();
232 }
233 
234 scoped_ptr<DownloadUIController::Delegate>
GetTestDelegate()235 DownloadUIControllerTest::GetTestDelegate() {
236   scoped_ptr<DownloadUIController::Delegate> delegate(
237       new TestDelegate(notified_item_receiver_factory_.GetWeakPtr()));
238   return delegate.Pass();
239 }
240 
241 // New downloads should be presented to the UI when GetTargetFilePath() returns
242 // a non-empty path.  I.e. once the download target has been determined.
TEST_F(DownloadUIControllerTest,DownloadUIController_NotifyBasic)243 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic) {
244   scoped_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
245   DownloadUIController controller(manager(), GetTestDelegate());
246   EXPECT_CALL(*item, GetTargetFilePath())
247       .WillOnce(ReturnRefOfCopy(base::FilePath()));
248 
249   ASSERT_TRUE(manager_observer());
250   manager_observer()->OnDownloadCreated(manager(), item.get());
251 
252   // The destination for the download hasn't been determined yet. It should not
253   // be displayed.
254   EXPECT_FALSE(notified_item());
255 
256   // Once the destination has been determined, then it should be displayed.
257   EXPECT_CALL(*item, GetTargetFilePath())
258       .WillOnce(ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
259   item->NotifyObserversDownloadUpdated();
260 
261   EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item());
262 }
263 
264 // A download that's created in an interrupted state should also be displayed.
TEST_F(DownloadUIControllerTest,DownloadUIController_NotifyBasic_Interrupted)265 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic_Interrupted) {
266   scoped_ptr<MockDownloadItem> item = CreateMockInProgressDownload();
267   DownloadUIController controller(manager(), GetTestDelegate());
268   EXPECT_CALL(*item, GetState())
269       .WillRepeatedly(Return(content::DownloadItem::INTERRUPTED));
270 
271   ASSERT_TRUE(manager_observer());
272   manager_observer()->OnDownloadCreated(manager(), item.get());
273   EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item());
274 }
275 
276 // Downloads that have a target path on creation and are in the IN_PROGRESS
277 // state should be displayed in the UI immediately without requiring an
278 // additional OnDownloadUpdated() notification.
TEST_F(DownloadUIControllerTest,DownloadUIController_NotifyReadyOnCreate)279 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyReadyOnCreate) {
280   scoped_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
281   DownloadUIController controller(manager(), GetTestDelegate());
282 
283   ASSERT_TRUE(manager_observer());
284   manager_observer()->OnDownloadCreated(manager(), item.get());
285   EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item());
286 }
287 
288 // The UI shouldn't be notified of downloads that were restored from history.
TEST_F(DownloadUIControllerTest,DownloadUIController_HistoryDownload)289 TEST_F(DownloadUIControllerTest, DownloadUIController_HistoryDownload) {
290   DownloadUIController controller(manager(), GetTestDelegate());
291   // DownloadHistory should already have been created. It performs a query of
292   // existing downloads upon creation. We'll use the callback to inject a
293   // history download.
294   ASSERT_FALSE(history_query_callback().is_null());
295 
296   // download_history_manager_observer is the DownloadManager::Observer
297   // registered by the DownloadHistory. DownloadHistory relies on the
298   // OnDownloadCreated notification to mark a download as having been restored
299   // from history.
300   ASSERT_TRUE(download_history_manager_observer());
301 
302   scoped_ptr<std::vector<history::DownloadRow> > history_downloads;
303   history_downloads.reset(new std::vector<history::DownloadRow>());
304   history_downloads->push_back(history::DownloadRow());
305   history_downloads->front().id = 1;
306 
307   std::vector<GURL> url_chain;
308   GURL url;
309   scoped_ptr<MockDownloadItem> item = CreateMockInProgressDownload();
310 
311   EXPECT_CALL(*item, GetOriginalMimeType());
312   EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
313 
314   {
315     testing::InSequence s;
316     testing::MockFunction<void()> mock_function;
317     // DownloadHistory will immediately try to create a download using the info
318     // we push through the query callback. When DownloadManager::CreateDownload
319     // is called, we need to first invoke the OnDownloadCreated callback for
320     // DownloadHistory before returning the DownloadItem since that's the
321     // sequence of events expected by DownloadHistory.
322     base::Closure history_on_created_callback =
323         base::Bind(&content::DownloadManager::Observer::OnDownloadCreated,
324                    base::Unretained(download_history_manager_observer()),
325                    manager(),
326                    item.get());
327     EXPECT_CALL(*manager(), MockCreateDownloadItem(_)).WillOnce(
328         testing::DoAll(testing::InvokeWithoutArgs(&history_on_created_callback,
329                                                   &base::Closure::Run),
330                        Return(item.get())));
331     EXPECT_CALL(mock_function, Call());
332 
333     history_query_callback().Run(history_downloads.Pass());
334     mock_function.Call();
335   }
336 
337   // Now pass along the notification to the OnDownloadCreated observer from
338   // DownloadUIController. It should ignore the download since it's marked as
339   // being restored from history.
340   ASSERT_TRUE(manager_observer());
341   manager_observer()->OnDownloadCreated(manager(), item.get());
342 
343   // Finally, the expectation we've been waiting for:
344   EXPECT_FALSE(notified_item());
345 }
346 
347 } // namespace
348