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 <vector>
6
7#include "base/memory/weak_ptr.h"
8#include "base/rand_util.h"
9#include "base/stl_util.h"
10#include "chrome/browser/download/download_history.h"
11#include "chrome/browser/history/download_database.h"
12#include "chrome/browser/history/download_row.h"
13#include "chrome/browser/history/history_service.h"
14#include "content/public/test/mock_download_item.h"
15#include "content/public/test/mock_download_manager.h"
16#include "content/public/test/test_browser_thread.h"
17#include "content/public/test/test_utils.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20#if defined(ENABLE_EXTENSIONS)
21#include "chrome/browser/extensions/api/downloads/downloads_api.h"
22#endif
23
24using testing::DoAll;
25using testing::Invoke;
26using testing::Return;
27using testing::ReturnRefOfCopy;
28using testing::SetArgPointee;
29using testing::WithArg;
30using testing::_;
31
32namespace {
33
34void CheckInfoEqual(const history::DownloadRow& left,
35                    const history::DownloadRow& right) {
36  EXPECT_EQ(left.current_path.value(), right.current_path.value());
37  EXPECT_EQ(left.target_path.value(), right.target_path.value());
38  EXPECT_EQ(left.url_chain.size(), right.url_chain.size());
39  for (unsigned int i = 0;
40       i < left.url_chain.size() && i < right.url_chain.size();
41       ++i) {
42    EXPECT_EQ(left.url_chain[i].spec(), right.url_chain[i].spec());
43  }
44  EXPECT_EQ(left.referrer_url.spec(), right.referrer_url.spec());
45  EXPECT_EQ(left.mime_type, right.mime_type);
46  EXPECT_EQ(left.original_mime_type, right.original_mime_type);
47  EXPECT_EQ(left.start_time.ToTimeT(), right.start_time.ToTimeT());
48  EXPECT_EQ(left.end_time.ToTimeT(), right.end_time.ToTimeT());
49  EXPECT_EQ(left.etag, right.etag);
50  EXPECT_EQ(left.last_modified, right.last_modified);
51  EXPECT_EQ(left.received_bytes, right.received_bytes);
52  EXPECT_EQ(left.total_bytes, right.total_bytes);
53  EXPECT_EQ(left.state, right.state);
54  EXPECT_EQ(left.danger_type, right.danger_type);
55  EXPECT_EQ(left.id, right.id);
56  EXPECT_EQ(left.opened, right.opened);
57  EXPECT_EQ(left.by_ext_id, right.by_ext_id);
58  EXPECT_EQ(left.by_ext_name, right.by_ext_name);
59}
60
61typedef DownloadHistory::IdSet IdSet;
62typedef std::vector<history::DownloadRow> InfoVector;
63typedef testing::StrictMock<content::MockDownloadItem> StrictMockDownloadItem;
64
65class FakeHistoryAdapter : public DownloadHistory::HistoryAdapter {
66 public:
67  FakeHistoryAdapter()
68    : DownloadHistory::HistoryAdapter(NULL),
69      slow_create_download_(false),
70      fail_create_download_(false) {
71  }
72
73  virtual ~FakeHistoryAdapter() {}
74
75  virtual void QueryDownloads(
76      const HistoryService::DownloadQueryCallback& callback) OVERRIDE {
77    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
78    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
79        base::Bind(&FakeHistoryAdapter::QueryDownloadsDone,
80            base::Unretained(this), callback));
81  }
82
83  void QueryDownloadsDone(
84      const HistoryService::DownloadQueryCallback& callback) {
85    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
86    CHECK(expect_query_downloads_.get());
87    callback.Run(expect_query_downloads_.Pass());
88  }
89
90  void set_slow_create_download(bool slow) { slow_create_download_ = slow; }
91
92  virtual void CreateDownload(
93      const history::DownloadRow& info,
94      const HistoryService::DownloadCreateCallback& callback) OVERRIDE {
95    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
96    create_download_info_ = info;
97    // Must not call CreateDownload() again before FinishCreateDownload()!
98    DCHECK(create_download_callback_.is_null());
99    create_download_callback_ = base::Bind(callback, !fail_create_download_);
100    fail_create_download_ = false;
101    if (!slow_create_download_)
102      FinishCreateDownload();
103  }
104
105  void FinishCreateDownload() {
106    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
107    create_download_callback_.Run();
108    create_download_callback_.Reset();
109  }
110
111  virtual void UpdateDownload(
112      const history::DownloadRow& info) OVERRIDE {
113    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
114    update_download_ = info;
115  }
116
117  virtual void RemoveDownloads(const IdSet& ids) OVERRIDE {
118    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
119    for (IdSet::const_iterator it = ids.begin();
120         it != ids.end(); ++it) {
121      remove_downloads_.insert(*it);
122    }
123  }
124
125  void ExpectWillQueryDownloads(scoped_ptr<InfoVector> infos) {
126    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
127    expect_query_downloads_ = infos.Pass();
128  }
129
130  void ExpectQueryDownloadsDone() {
131    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
132    EXPECT_TRUE(NULL == expect_query_downloads_.get());
133  }
134
135  void FailCreateDownload() {
136    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
137    fail_create_download_ = true;
138  }
139
140  void ExpectDownloadCreated(
141      const history::DownloadRow& info) {
142    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
143    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
144    CheckInfoEqual(info, create_download_info_);
145    create_download_info_ = history::DownloadRow();
146  }
147
148  void ExpectNoDownloadCreated() {
149    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
150    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
151    CheckInfoEqual(history::DownloadRow(), create_download_info_);
152  }
153
154  void ExpectDownloadUpdated(const history::DownloadRow& info) {
155    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
156    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
157    CheckInfoEqual(update_download_, info);
158    update_download_ = history::DownloadRow();
159  }
160
161  void ExpectNoDownloadUpdated() {
162    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
163    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
164    CheckInfoEqual(history::DownloadRow(), update_download_);
165  }
166
167  void ExpectNoDownloadsRemoved() {
168    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
169    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
170    EXPECT_EQ(0, static_cast<int>(remove_downloads_.size()));
171  }
172
173  void ExpectDownloadsRemoved(const IdSet& ids) {
174    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
175    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
176    IdSet differences = base::STLSetDifference<IdSet>(ids, remove_downloads_);
177    for (IdSet::const_iterator different = differences.begin();
178         different != differences.end(); ++different) {
179      EXPECT_TRUE(false) << *different;
180    }
181    remove_downloads_.clear();
182  }
183
184 private:
185  bool slow_create_download_;
186  bool fail_create_download_;
187  base::Closure create_download_callback_;
188  history::DownloadRow update_download_;
189  scoped_ptr<InfoVector> expect_query_downloads_;
190  IdSet remove_downloads_;
191  history::DownloadRow create_download_info_;
192
193  DISALLOW_COPY_AND_ASSIGN(FakeHistoryAdapter);
194};
195
196class DownloadHistoryTest : public testing::Test {
197 public:
198  // Generic callback that receives a pointer to a StrictMockDownloadItem.
199  typedef base::Callback<void(content::MockDownloadItem*)> DownloadItemCallback;
200
201  DownloadHistoryTest()
202      : ui_thread_(content::BrowserThread::UI, &loop_),
203        manager_(new content::MockDownloadManager()),
204        history_(NULL),
205        manager_observer_(NULL),
206        download_created_index_(0) {}
207  virtual ~DownloadHistoryTest() {
208    STLDeleteElements(&items_);
209  }
210
211 protected:
212  virtual void TearDown() OVERRIDE {
213    download_history_.reset();
214  }
215
216  content::MockDownloadManager& manager() { return *manager_.get(); }
217  content::MockDownloadItem& item(size_t index) { return *items_[index]; }
218  DownloadHistory* download_history() { return download_history_.get(); }
219
220  void SetManagerObserver(
221      content::DownloadManager::Observer* manager_observer) {
222    manager_observer_ = manager_observer;
223  }
224  content::DownloadManager::Observer* manager_observer() {
225    return manager_observer_;
226  }
227
228  void CreateDownloadHistory(scoped_ptr<InfoVector> infos) {
229    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
230    CHECK(infos.get());
231    EXPECT_CALL(manager(), AddObserver(_)).WillOnce(WithArg<0>(Invoke(
232        this, &DownloadHistoryTest::SetManagerObserver)));
233    EXPECT_CALL(manager(), RemoveObserver(_));
234    download_created_index_ = 0;
235    for (size_t index = 0; index < infos->size(); ++index) {
236      content::MockDownloadManager::CreateDownloadItemAdapter adapter(
237          infos->at(index).id,
238          infos->at(index).current_path,
239          infos->at(index).target_path,
240          infos->at(index).url_chain,
241          infos->at(index).referrer_url,
242          infos->at(index).mime_type,
243          infos->at(index).original_mime_type,
244          infos->at(index).start_time,
245          infos->at(index).end_time,
246          infos->at(index).etag,
247          infos->at(index).last_modified,
248          infos->at(index).received_bytes,
249          infos->at(index).total_bytes,
250          infos->at(index).state,
251          infos->at(index).danger_type,
252          infos->at(index).interrupt_reason,
253          infos->at(index).opened);
254      EXPECT_CALL(manager(), MockCreateDownloadItem(adapter))
255        .WillOnce(DoAll(
256            InvokeWithoutArgs(
257                this, &DownloadHistoryTest::CallOnDownloadCreatedInOrder),
258            Return(&item(index))));
259    }
260    EXPECT_CALL(manager(), CheckForHistoryFilesRemoval());
261    history_ = new FakeHistoryAdapter();
262    history_->ExpectWillQueryDownloads(infos.Pass());
263    EXPECT_CALL(*manager_.get(), GetAllDownloads(_)).WillRepeatedly(Return());
264    download_history_.reset(new DownloadHistory(
265        &manager(), scoped_ptr<DownloadHistory::HistoryAdapter>(history_)));
266    content::RunAllPendingInMessageLoop(content::BrowserThread::UI);
267    history_->ExpectQueryDownloadsDone();
268  }
269
270  void CallOnDownloadCreated(size_t index) {
271    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
272    if (!pre_on_create_handler_.is_null())
273      pre_on_create_handler_.Run(&item(index));
274    manager_observer()->OnDownloadCreated(&manager(), &item(index));
275    if (!post_on_create_handler_.is_null())
276      post_on_create_handler_.Run(&item(index));
277  }
278
279  void CallOnDownloadCreatedInOrder() {
280    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
281    // Gmock doesn't appear to support something like InvokeWithTheseArgs. Maybe
282    // gmock needs to learn about base::Callback.
283    CallOnDownloadCreated(download_created_index_++);
284  }
285
286  void set_slow_create_download(bool slow) {
287    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
288    history_->set_slow_create_download(slow);
289  }
290
291  void FinishCreateDownload() {
292    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
293    history_->FinishCreateDownload();
294  }
295
296  void FailCreateDownload() {
297    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
298    history_->FailCreateDownload();
299  }
300
301  void ExpectDownloadCreated(
302      const history::DownloadRow& info) {
303    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
304    history_->ExpectDownloadCreated(info);
305  }
306
307  void ExpectNoDownloadCreated() {
308    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
309    history_->ExpectNoDownloadCreated();
310  }
311
312  void ExpectDownloadUpdated(const history::DownloadRow& info) {
313    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
314    history_->ExpectDownloadUpdated(info);
315  }
316
317  void ExpectNoDownloadUpdated() {
318    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
319    history_->ExpectNoDownloadUpdated();
320  }
321
322  void ExpectNoDownloadsRemoved() {
323    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
324    history_->ExpectNoDownloadsRemoved();
325  }
326
327  void ExpectDownloadsRemoved(const IdSet& ids) {
328    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
329    history_->ExpectDownloadsRemoved(ids);
330  }
331
332  void ExpectDownloadsRestoredFromHistory(bool expected_value) {
333    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
334    pre_on_create_handler_ =
335        base::Bind(&DownloadHistoryTest::CheckDownloadWasRestoredFromHistory,
336                   base::Unretained(this),
337                   expected_value);
338    post_on_create_handler_ =
339        base::Bind(&DownloadHistoryTest::CheckDownloadWasRestoredFromHistory,
340                   base::Unretained(this),
341                   expected_value);
342  }
343
344  void InitBasicItem(const base::FilePath::CharType* path,
345                     const char* url_string,
346                     const char* referrer_string,
347                     history::DownloadRow* info) {
348    GURL url(url_string);
349    GURL referrer(referrer_string);
350    std::vector<GURL> url_chain;
351    url_chain.push_back(url);
352    InitItem(static_cast<uint32>(items_.size() + 1),
353             base::FilePath(path),
354             base::FilePath(path),
355             url_chain,
356             referrer,
357             "application/octet-stream",
358             "application/octet-stream",
359             (base::Time::Now() - base::TimeDelta::FromMinutes(10)),
360             (base::Time::Now() - base::TimeDelta::FromMinutes(1)),
361             "Etag",
362             "abc",
363             100,
364             100,
365             content::DownloadItem::COMPLETE,
366             content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
367             content::DOWNLOAD_INTERRUPT_REASON_NONE,
368             false,
369             std::string(),
370             std::string(),
371             info);
372  }
373
374  void InitItem(
375      uint32 id,
376      const base::FilePath& current_path,
377      const base::FilePath& target_path,
378      const std::vector<GURL>& url_chain,
379      const GURL& referrer,
380      const std::string& mime_type,
381      const std::string& original_mime_type,
382      const base::Time& start_time,
383      const base::Time& end_time,
384      const std::string& etag,
385      const std::string& last_modified,
386      int64 received_bytes,
387      int64 total_bytes,
388      content::DownloadItem::DownloadState state,
389      content::DownloadDangerType danger_type,
390      content::DownloadInterruptReason interrupt_reason,
391      bool opened,
392      const std::string& by_extension_id,
393      const std::string& by_extension_name,
394      history::DownloadRow* info) {
395    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
396
397    size_t index = items_.size();
398    StrictMockDownloadItem* mock_item = new StrictMockDownloadItem();
399    items_.push_back(mock_item);
400
401    info->current_path = current_path;
402    info->target_path = target_path;
403    info->url_chain = url_chain;
404    info->referrer_url = referrer;
405    info->mime_type = mime_type;
406    info->original_mime_type = original_mime_type;
407    info->start_time = start_time;
408    info->end_time = end_time;
409    info->etag = etag;
410    info->last_modified = last_modified;
411    info->received_bytes = received_bytes;
412    info->total_bytes = total_bytes;
413    info->state = state;
414    info->danger_type = danger_type;
415    info->interrupt_reason = interrupt_reason;
416    info->id = id;
417    info->opened = opened;
418    info->by_ext_id = by_extension_id;
419    info->by_ext_name = by_extension_name;
420
421    EXPECT_CALL(item(index), GetId()).WillRepeatedly(Return(id));
422    EXPECT_CALL(item(index), GetFullPath())
423        .WillRepeatedly(ReturnRefOfCopy(current_path));
424    EXPECT_CALL(item(index), GetTargetFilePath())
425        .WillRepeatedly(ReturnRefOfCopy(target_path));
426    DCHECK_LE(1u, url_chain.size());
427    EXPECT_CALL(item(index), GetURL())
428        .WillRepeatedly(ReturnRefOfCopy(url_chain[0]));
429    EXPECT_CALL(item(index), GetUrlChain())
430        .WillRepeatedly(ReturnRefOfCopy(url_chain));
431    EXPECT_CALL(item(index), GetMimeType()).WillRepeatedly(Return(mime_type));
432    EXPECT_CALL(item(index), GetOriginalMimeType()).WillRepeatedly(Return(
433        original_mime_type));
434    EXPECT_CALL(item(index), GetReferrerUrl())
435        .WillRepeatedly(ReturnRefOfCopy(referrer));
436    EXPECT_CALL(item(index), GetStartTime()).WillRepeatedly(Return(start_time));
437    EXPECT_CALL(item(index), GetEndTime()).WillRepeatedly(Return(end_time));
438    EXPECT_CALL(item(index), GetETag()).WillRepeatedly(ReturnRefOfCopy(etag));
439    EXPECT_CALL(item(index), GetLastModifiedTime())
440        .WillRepeatedly(ReturnRefOfCopy(last_modified));
441    EXPECT_CALL(item(index), GetReceivedBytes())
442        .WillRepeatedly(Return(received_bytes));
443    EXPECT_CALL(item(index), GetTotalBytes())
444        .WillRepeatedly(Return(total_bytes));
445    EXPECT_CALL(item(index), GetState()).WillRepeatedly(Return(state));
446    EXPECT_CALL(item(index), GetDangerType())
447        .WillRepeatedly(Return(danger_type));
448    EXPECT_CALL(item(index), GetLastReason())
449        .WillRepeatedly(Return(interrupt_reason));
450    EXPECT_CALL(item(index), GetOpened()).WillRepeatedly(Return(opened));
451    EXPECT_CALL(item(index), GetTargetDisposition())
452        .WillRepeatedly(
453            Return(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE));
454    EXPECT_CALL(manager(), GetDownload(id))
455        .WillRepeatedly(Return(&item(index)));
456    EXPECT_CALL(item(index), IsTemporary()).WillRepeatedly(Return(false));
457#if defined(ENABLE_EXTENSIONS)
458    new extensions::DownloadedByExtension(
459        &item(index), by_extension_id, by_extension_name);
460#endif
461
462    std::vector<content::DownloadItem*> items;
463    for (size_t i = 0; i < items_.size(); ++i) {
464      items.push_back(&item(i));
465    }
466    EXPECT_CALL(*manager_.get(), GetAllDownloads(_))
467        .WillRepeatedly(SetArgPointee<0>(items));
468  }
469
470 private:
471  void CheckDownloadWasRestoredFromHistory(bool expected_value,
472                                           content::MockDownloadItem* item) {
473    ASSERT_TRUE(download_history_.get());
474    EXPECT_EQ(expected_value, download_history_->WasRestoredFromHistory(item));
475  }
476
477  base::MessageLoopForUI loop_;
478  content::TestBrowserThread ui_thread_;
479  std::vector<StrictMockDownloadItem*> items_;
480  scoped_ptr<content::MockDownloadManager> manager_;
481  FakeHistoryAdapter* history_;
482  scoped_ptr<DownloadHistory> download_history_;
483  content::DownloadManager::Observer* manager_observer_;
484  size_t download_created_index_;
485  DownloadItemCallback pre_on_create_handler_;
486  DownloadItemCallback post_on_create_handler_;
487
488  DISALLOW_COPY_AND_ASSIGN(DownloadHistoryTest);
489};
490
491// Test loading an item from the database, changing it, saving it back, removing
492// it.
493TEST_F(DownloadHistoryTest, DownloadHistoryTest_Load) {
494  // Load a download from history, create the item, OnDownloadCreated,
495  // OnDownloadUpdated, OnDownloadRemoved.
496  history::DownloadRow info;
497  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
498                "http://example.com/bar.pdf",
499                "http://example.com/referrer.html",
500                &info);
501  {
502    scoped_ptr<InfoVector> infos(new InfoVector());
503    infos->push_back(info);
504    CreateDownloadHistory(infos.Pass());
505    ExpectNoDownloadCreated();
506  }
507  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
508
509  // Pretend that something changed on the item.
510  EXPECT_CALL(item(0), GetOpened()).WillRepeatedly(Return(true));
511  item(0).NotifyObserversDownloadUpdated();
512  info.opened = true;
513  ExpectDownloadUpdated(info);
514
515  // Pretend that the user removed the item.
516  IdSet ids;
517  ids.insert(info.id);
518  item(0).NotifyObserversDownloadRemoved();
519  ExpectDownloadsRemoved(ids);
520}
521
522// Test that WasRestoredFromHistory accurately identifies downloads that were
523// created from history, even during an OnDownloadCreated() handler.
524TEST_F(DownloadHistoryTest, DownloadHistoryTest_WasRestoredFromHistory_True) {
525  // This sets DownloadHistoryTest to call DH::WasRestoredFromHistory() both
526  // before and after DH::OnDownloadCreated() is called. At each call, the
527  // expected return value is |true| since the download was restored from
528  // history.
529  ExpectDownloadsRestoredFromHistory(true);
530
531  // Construct a DownloadHistory with a single history download. This results in
532  // DownloadManager::CreateDownload() being called for the restored download.
533  // The above test expectation should verify that the value of
534  // WasRestoredFromHistory is correct for this download.
535  history::DownloadRow info;
536  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
537                "http://example.com/bar.pdf",
538                "http://example.com/referrer.html",
539                &info);
540  scoped_ptr<InfoVector> infos(new InfoVector());
541  infos->push_back(info);
542  CreateDownloadHistory(infos.Pass());
543
544  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
545}
546
547// Test that WasRestoredFromHistory accurately identifies downloads that were
548// not created from history.
549TEST_F(DownloadHistoryTest, DownloadHistoryTest_WasRestoredFromHistory_False) {
550  // This sets DownloadHistoryTest to call DH::WasRestoredFromHistory() both
551  // before and after DH::OnDownloadCreated() is called. At each call, the
552  // expected return value is |true| since the download was restored from
553  // history.
554  ExpectDownloadsRestoredFromHistory(false);
555
556  // Create a DownloadHistory with no history downloads. No
557  // DownloadManager::CreateDownload() calls are expected.
558  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
559
560  // Notify DownloadHistory that a new download was created. The above test
561  // expecation should verify that WasRestoredFromHistory is correct for this
562  // download.
563  history::DownloadRow info;
564  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
565                "http://example.com/bar.pdf",
566                "http://example.com/referrer.html",
567                &info);
568  CallOnDownloadCreated(0);
569  ExpectDownloadCreated(info);
570}
571
572// Test creating an item, saving it to the database, changing it, saving it
573// back, removing it.
574TEST_F(DownloadHistoryTest, DownloadHistoryTest_Create) {
575  // Create a fresh item not from history, OnDownloadCreated, OnDownloadUpdated,
576  // OnDownloadRemoved.
577  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
578
579  history::DownloadRow info;
580  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
581                "http://example.com/bar.pdf",
582                "http://example.com/referrer.html",
583                &info);
584
585  // Pretend the manager just created |item|.
586  CallOnDownloadCreated(0);
587  ExpectDownloadCreated(info);
588  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
589
590  // Pretend that something changed on the item.
591  EXPECT_CALL(item(0), GetOpened()).WillRepeatedly(Return(true));
592  item(0).NotifyObserversDownloadUpdated();
593  info.opened = true;
594  ExpectDownloadUpdated(info);
595
596  // Pretend that the user removed the item.
597  IdSet ids;
598  ids.insert(info.id);
599  item(0).NotifyObserversDownloadRemoved();
600  ExpectDownloadsRemoved(ids);
601}
602
603// Test that changes to persisted fields in a DownloadItem triggers database
604// updates.
605TEST_F(DownloadHistoryTest, DownloadHistoryTest_Update) {
606  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
607
608  history::DownloadRow info;
609  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
610                "http://example.com/bar.pdf",
611                "http://example.com/referrer.html",
612                &info);
613  CallOnDownloadCreated(0);
614  ExpectDownloadCreated(info);
615  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
616
617  base::FilePath new_path(FILE_PATH_LITERAL("/foo/baz.txt"));
618  base::Time new_time(base::Time::Now());
619  std::string new_etag("new etag");
620  std::string new_last_modifed("new last modified");
621
622  // current_path
623  EXPECT_CALL(item(0), GetFullPath()).WillRepeatedly(ReturnRefOfCopy(new_path));
624  info.current_path = new_path;
625  item(0).NotifyObserversDownloadUpdated();
626  ExpectDownloadUpdated(info);
627
628  // target_path
629  EXPECT_CALL(item(0), GetTargetFilePath())
630      .WillRepeatedly(ReturnRefOfCopy(new_path));
631  info.target_path = new_path;
632  item(0).NotifyObserversDownloadUpdated();
633  ExpectDownloadUpdated(info);
634
635  // end_time
636  EXPECT_CALL(item(0), GetEndTime()).WillRepeatedly(Return(new_time));
637  info.end_time = new_time;
638  item(0).NotifyObserversDownloadUpdated();
639  ExpectDownloadUpdated(info);
640
641  // received_bytes
642  EXPECT_CALL(item(0), GetReceivedBytes()).WillRepeatedly(Return(101));
643  info.received_bytes = 101;
644  item(0).NotifyObserversDownloadUpdated();
645  ExpectDownloadUpdated(info);
646
647  // total_bytes
648  EXPECT_CALL(item(0), GetTotalBytes()).WillRepeatedly(Return(102));
649  info.total_bytes = 102;
650  item(0).NotifyObserversDownloadUpdated();
651  ExpectDownloadUpdated(info);
652
653  // etag
654  EXPECT_CALL(item(0), GetETag()).WillRepeatedly(ReturnRefOfCopy(new_etag));
655  info.etag = new_etag;
656  item(0).NotifyObserversDownloadUpdated();
657  ExpectDownloadUpdated(info);
658
659  // last_modified
660  EXPECT_CALL(item(0), GetLastModifiedTime())
661      .WillRepeatedly(ReturnRefOfCopy(new_last_modifed));
662  info.last_modified = new_last_modifed;
663  item(0).NotifyObserversDownloadUpdated();
664  ExpectDownloadUpdated(info);
665
666  // state
667  EXPECT_CALL(item(0), GetState())
668      .WillRepeatedly(Return(content::DownloadItem::INTERRUPTED));
669  info.state = content::DownloadItem::INTERRUPTED;
670  item(0).NotifyObserversDownloadUpdated();
671  ExpectDownloadUpdated(info);
672
673  // danger_type
674  EXPECT_CALL(item(0), GetDangerType())
675      .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT));
676  info.danger_type = content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT;
677  item(0).NotifyObserversDownloadUpdated();
678  ExpectDownloadUpdated(info);
679
680  // interrupt_reason
681  EXPECT_CALL(item(0), GetLastReason())
682      .WillRepeatedly(Return(content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED));
683  info.interrupt_reason = content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
684  item(0).NotifyObserversDownloadUpdated();
685  ExpectDownloadUpdated(info);
686
687  // opened
688  EXPECT_CALL(item(0), GetOpened()).WillRepeatedly(Return(true));
689  info.opened = true;
690  item(0).NotifyObserversDownloadUpdated();
691  ExpectDownloadUpdated(info);
692}
693
694// Test creating a new item, saving it, removing it by setting it Temporary,
695// changing it without saving it back because it's Temporary, clearing
696// IsTemporary, saving it back, changing it, saving it back because it isn't
697// Temporary anymore.
698TEST_F(DownloadHistoryTest, DownloadHistoryTest_Temporary) {
699  // Create a fresh item not from history, OnDownloadCreated, OnDownloadUpdated,
700  // OnDownloadRemoved.
701  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
702
703  history::DownloadRow info;
704  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
705                "http://example.com/bar.pdf",
706                "http://example.com/referrer.html",
707                &info);
708
709  // Pretend the manager just created |item|.
710  CallOnDownloadCreated(0);
711  ExpectDownloadCreated(info);
712  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
713
714  // Pretend the item was marked temporary. DownloadHistory should remove it
715  // from history and start ignoring it.
716  EXPECT_CALL(item(0), IsTemporary()).WillRepeatedly(Return(true));
717  item(0).NotifyObserversDownloadUpdated();
718  IdSet ids;
719  ids.insert(info.id);
720  ExpectDownloadsRemoved(ids);
721
722  // Change something that would make DownloadHistory call UpdateDownload if the
723  // item weren't temporary.
724  EXPECT_CALL(item(0), GetReceivedBytes()).WillRepeatedly(Return(4200));
725  item(0).NotifyObserversDownloadUpdated();
726  ExpectNoDownloadUpdated();
727
728  // Changing a temporary item back to a non-temporary item should make
729  // DownloadHistory call CreateDownload.
730  EXPECT_CALL(item(0), IsTemporary()).WillRepeatedly(Return(false));
731  item(0).NotifyObserversDownloadUpdated();
732  info.received_bytes = 4200;
733  ExpectDownloadCreated(info);
734  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
735
736  EXPECT_CALL(item(0), GetReceivedBytes()).WillRepeatedly(Return(100));
737  item(0).NotifyObserversDownloadUpdated();
738  info.received_bytes = 100;
739  ExpectDownloadUpdated(info);
740}
741
742// Test removing downloads while they're still being added.
743TEST_F(DownloadHistoryTest, DownloadHistoryTest_RemoveWhileAdding) {
744  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
745
746  history::DownloadRow info;
747  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
748                "http://example.com/bar.pdf",
749                "http://example.com/referrer.html",
750                &info);
751
752  // Instruct CreateDownload() to not callback to DownloadHistory immediately,
753  // but to wait for FinishCreateDownload().
754  set_slow_create_download(true);
755
756  // Pretend the manager just created |item|.
757  CallOnDownloadCreated(0);
758  ExpectDownloadCreated(info);
759  EXPECT_FALSE(DownloadHistory::IsPersisted(&item(0)));
760
761  // Call OnDownloadRemoved before calling back to DownloadHistory::ItemAdded().
762  // Instead of calling RemoveDownloads() immediately, DownloadHistory should
763  // add the item's id to removed_while_adding_. Then, ItemAdded should
764  // immediately remove the item's record from history.
765  item(0).NotifyObserversDownloadRemoved();
766  EXPECT_CALL(manager(), GetDownload(item(0).GetId()))
767    .WillRepeatedly(Return(static_cast<content::DownloadItem*>(NULL)));
768  ExpectNoDownloadsRemoved();
769  EXPECT_FALSE(DownloadHistory::IsPersisted(&item(0)));
770
771  // Now callback to DownloadHistory::ItemAdded(), and expect a call to
772  // RemoveDownloads() for the item that was removed while it was being added.
773  FinishCreateDownload();
774  IdSet ids;
775  ids.insert(info.id);
776  ExpectDownloadsRemoved(ids);
777  EXPECT_FALSE(DownloadHistory::IsPersisted(&item(0)));
778}
779
780// Test loading multiple items from the database and removing them all.
781TEST_F(DownloadHistoryTest, DownloadHistoryTest_Multiple) {
782  // Load a download from history, create the item, OnDownloadCreated,
783  // OnDownloadUpdated, OnDownloadRemoved.
784  history::DownloadRow info0, info1;
785  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
786                "http://example.com/bar.pdf",
787                "http://example.com/referrer.html",
788                &info0);
789  InitBasicItem(FILE_PATH_LITERAL("/foo/qux.pdf"),
790                "http://example.com/qux.pdf",
791                "http://example.com/referrer1.html",
792                &info1);
793  {
794    scoped_ptr<InfoVector> infos(new InfoVector());
795    infos->push_back(info0);
796    infos->push_back(info1);
797    CreateDownloadHistory(infos.Pass());
798    ExpectNoDownloadCreated();
799  }
800
801  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
802  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(1)));
803
804  // Pretend that the user removed both items.
805  IdSet ids;
806  ids.insert(info0.id);
807  ids.insert(info1.id);
808  item(0).NotifyObserversDownloadRemoved();
809  item(1).NotifyObserversDownloadRemoved();
810  ExpectDownloadsRemoved(ids);
811}
812
813// Test what happens when HistoryService/CreateDownload::CreateDownload() fails.
814TEST_F(DownloadHistoryTest, DownloadHistoryTest_CreateFailed) {
815  // Create a fresh item not from history, OnDownloadCreated, OnDownloadUpdated,
816  // OnDownloadRemoved.
817  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
818
819  history::DownloadRow info;
820  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
821                "http://example.com/bar.pdf",
822                "http://example.com/referrer.html",
823                &info);
824
825  FailCreateDownload();
826  // Pretend the manager just created |item|.
827  CallOnDownloadCreated(0);
828  ExpectDownloadCreated(info);
829  EXPECT_FALSE(DownloadHistory::IsPersisted(&item(0)));
830
831  EXPECT_CALL(item(0), GetReceivedBytes()).WillRepeatedly(Return(100));
832  item(0).NotifyObserversDownloadUpdated();
833  info.received_bytes = 100;
834  ExpectDownloadCreated(info);
835  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
836}
837
838TEST_F(DownloadHistoryTest, DownloadHistoryTest_UpdateWhileAdding) {
839  // Create a fresh item not from history, OnDownloadCreated, OnDownloadUpdated,
840  // OnDownloadRemoved.
841  CreateDownloadHistory(scoped_ptr<InfoVector>(new InfoVector()));
842
843  history::DownloadRow info;
844  InitBasicItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
845                "http://example.com/bar.pdf",
846                "http://example.com/referrer.html",
847                &info);
848
849  // Instruct CreateDownload() to not callback to DownloadHistory immediately,
850  // but to wait for FinishCreateDownload().
851  set_slow_create_download(true);
852
853  // Pretend the manager just created |item|.
854  CallOnDownloadCreated(0);
855  ExpectDownloadCreated(info);
856  EXPECT_FALSE(DownloadHistory::IsPersisted(&item(0)));
857
858  // Pretend that something changed on the item.
859  EXPECT_CALL(item(0), GetOpened()).WillRepeatedly(Return(true));
860  item(0).NotifyObserversDownloadUpdated();
861
862  FinishCreateDownload();
863  EXPECT_TRUE(DownloadHistory::IsPersisted(&item(0)));
864
865  // ItemAdded should call OnDownloadUpdated, which should detect that the item
866  // changed while it was being added and call UpdateDownload immediately.
867  info.opened = true;
868  ExpectDownloadUpdated(info);
869}
870
871}  // anonymous namespace
872