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 "chrome/browser/importer/profile_writer.h"
6
7#include <string>
8
9#include "base/message_loop/message_loop.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/bookmarks/bookmark_model_factory.h"
12#include "chrome/browser/history/history_service.h"
13#include "chrome/browser/history/history_service_factory.h"
14#include "chrome/browser/importer/importer_unittest_utils.h"
15#include "chrome/common/importer/imported_bookmark_entry.h"
16#include "chrome/test/base/testing_profile.h"
17#include "components/bookmarks/browser/bookmark_match.h"
18#include "components/bookmarks/browser/bookmark_model.h"
19#include "components/bookmarks/browser/bookmark_utils.h"
20#include "components/bookmarks/test/bookmark_test_helpers.h"
21#include "components/history/core/browser/history_types.h"
22#include "content/public/test/test_browser_thread.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25using bookmarks::BookmarkMatch;
26using content::BrowserThread;
27
28class TestProfileWriter : public ProfileWriter {
29 public:
30  explicit TestProfileWriter(Profile* profile) : ProfileWriter(profile) {}
31 protected:
32  virtual ~TestProfileWriter() {}
33};
34
35class ProfileWriterTest : public testing::Test {
36 public:
37  ProfileWriterTest()
38      : ui_thread_(BrowserThread::UI, &loop_),
39        file_thread_(BrowserThread::FILE, &loop_) {
40  }
41  virtual ~ProfileWriterTest() {}
42
43  // Create test bookmark entries to be added to ProfileWriter to
44  // simulate bookmark importing.
45  void CreateImportedBookmarksEntries() {
46    AddImportedBookmarkEntry(GURL("http://www.google.com"),
47                             base::ASCIIToUTF16("Google"));
48    AddImportedBookmarkEntry(GURL("http://www.yahoo.com"),
49                             base::ASCIIToUTF16("Yahoo"));
50  }
51
52  // Helper function to create history entries.
53  history::URLRow MakeURLRow(const char* url,
54                             base::string16 title,
55                             int visit_count,
56                             int days_since_last_visit,
57                             int typed_count) {
58    history::URLRow row(GURL(url), 0);
59    row.set_title(title);
60    row.set_visit_count(visit_count);
61    row.set_typed_count(typed_count);
62    row.set_last_visit(base::Time::NowFromSystemTime() -
63                       base::TimeDelta::FromDays(days_since_last_visit));
64    return row;
65  }
66
67  // Create test history entries to be added to ProfileWriter to
68  // simulate history importing.
69  void CreateHistoryPageEntries() {
70    history::URLRow row1(
71        MakeURLRow("http://www.google.com", base::ASCIIToUTF16("Google"),
72        3, 10, 1));
73    history::URLRow row2(
74        MakeURLRow("http://www.yahoo.com", base::ASCIIToUTF16("Yahoo"),
75        3, 30, 10));
76    pages_.push_back(row1);
77    pages_.push_back(row2);
78  }
79
80  void VerifyBookmarksCount(
81      const std::vector<BookmarkModel::URLAndTitle>& bookmarks_record,
82      BookmarkModel* bookmark_model,
83      size_t expected) {
84    std::vector<BookmarkMatch> matches;
85    for (size_t i = 0; i < bookmarks_record.size(); ++i) {
86      bookmark_model->GetBookmarksMatching(
87          bookmarks_record[i].title, 10, &matches);
88      EXPECT_EQ(expected, matches.size());
89      matches.clear();
90    }
91  }
92
93  void VerifyHistoryCount(Profile* profile) {
94    HistoryService* history_service =
95        HistoryServiceFactory::GetForProfile(profile,
96                                             Profile::EXPLICIT_ACCESS);
97    history::QueryOptions options;
98    base::CancelableTaskTracker history_task_tracker;
99    history_service->QueryHistory(
100        base::string16(),
101        options,
102        base::Bind(&ProfileWriterTest::HistoryQueryComplete,
103                   base::Unretained(this)),
104        &history_task_tracker);
105    base::MessageLoop::current()->Run();
106  }
107
108  void HistoryQueryComplete(history::QueryResults* results) {
109    base::MessageLoop::current()->Quit();
110    history_count_ = results->size();
111  }
112
113 protected:
114  std::vector<ImportedBookmarkEntry> bookmarks_;
115  history::URLRows pages_;
116  size_t history_count_;
117
118 private:
119  void AddImportedBookmarkEntry(const GURL& url, const base::string16& title) {
120    base::Time date;
121    ImportedBookmarkEntry entry;
122    entry.creation_time = date;
123    entry.url = url;
124    entry.title = title;
125    entry.in_toolbar = true;
126    entry.is_folder = false;
127    bookmarks_.push_back(entry);
128  }
129
130  base::MessageLoop loop_;
131  content::TestBrowserThread ui_thread_;
132  content::TestBrowserThread file_thread_;
133
134  DISALLOW_COPY_AND_ASSIGN(ProfileWriterTest);
135};
136
137// Add bookmarks via ProfileWriter to profile1 when profile2 also exists.
138TEST_F(ProfileWriterTest, CheckBookmarksWithMultiProfile) {
139  TestingProfile profile2;
140  profile2.CreateBookmarkModel(true);
141
142  BookmarkModel* bookmark_model2 =
143      BookmarkModelFactory::GetForProfile(&profile2);
144  test::WaitForBookmarkModelToLoad(bookmark_model2);
145  bookmarks::AddIfNotBookmarked(
146      bookmark_model2, GURL("http://www.bing.com"), base::ASCIIToUTF16("Bing"));
147  TestingProfile profile1;
148  profile1.CreateBookmarkModel(true);
149
150  CreateImportedBookmarksEntries();
151  BookmarkModel* bookmark_model1 =
152      BookmarkModelFactory::GetForProfile(&profile1);
153  test::WaitForBookmarkModelToLoad(bookmark_model1);
154
155  scoped_refptr<TestProfileWriter> profile_writer(
156      new TestProfileWriter(&profile1));
157  profile_writer->AddBookmarks(bookmarks_,
158                               base::ASCIIToUTF16("Imported from Firefox"));
159
160  std::vector<BookmarkModel::URLAndTitle> url_record1;
161  bookmark_model1->GetBookmarks(&url_record1);
162  EXPECT_EQ(2u, url_record1.size());
163
164  std::vector<BookmarkModel::URLAndTitle> url_record2;
165  bookmark_model2->GetBookmarks(&url_record2);
166  EXPECT_EQ(1u, url_record2.size());
167}
168
169// Verify that bookmarks are duplicated when added twice.
170TEST_F(ProfileWriterTest, CheckBookmarksAfterWritingDataTwice) {
171  TestingProfile profile;
172  profile.CreateBookmarkModel(true);
173
174  CreateImportedBookmarksEntries();
175  BookmarkModel* bookmark_model =
176      BookmarkModelFactory::GetForProfile(&profile);
177  test::WaitForBookmarkModelToLoad(bookmark_model);
178
179  scoped_refptr<TestProfileWriter> profile_writer(
180      new TestProfileWriter(&profile));
181  profile_writer->AddBookmarks(bookmarks_,
182                               base::ASCIIToUTF16("Imported from Firefox"));
183  std::vector<BookmarkModel::URLAndTitle> bookmarks_record;
184  bookmark_model->GetBookmarks(&bookmarks_record);
185  EXPECT_EQ(2u, bookmarks_record.size());
186
187  VerifyBookmarksCount(bookmarks_record, bookmark_model, 1);
188
189  profile_writer->AddBookmarks(bookmarks_,
190                               base::ASCIIToUTF16("Imported from Firefox"));
191  // Verify that duplicate bookmarks exist.
192  VerifyBookmarksCount(bookmarks_record, bookmark_model, 2);
193}
194
195// Verify that history entires are not duplicated when added twice.
196TEST_F(ProfileWriterTest, CheckHistoryAfterWritingDataTwice) {
197  TestingProfile profile;
198  ASSERT_TRUE(profile.CreateHistoryService(true, false));
199  profile.BlockUntilHistoryProcessesPendingRequests();
200
201  CreateHistoryPageEntries();
202  scoped_refptr<TestProfileWriter> profile_writer(
203      new TestProfileWriter(&profile));
204  profile_writer->AddHistoryPage(pages_, history::SOURCE_FIREFOX_IMPORTED);
205  VerifyHistoryCount(&profile);
206  size_t original_history_count = history_count_;
207  history_count_ = 0;
208
209  profile_writer->AddHistoryPage(pages_, history::SOURCE_FIREFOX_IMPORTED);
210  VerifyHistoryCount(&profile);
211  EXPECT_EQ(original_history_count, history_count_);
212}
213