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