Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / bookmarks / bookmark_index_unittest.cc
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 "chrome/browser/bookmarks/bookmark_index.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/bookmarks/bookmark_model.h"
16 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
17 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
18 #include "chrome/browser/bookmarks/bookmark_title_match.h"
19 #include "chrome/browser/history/history_service.h"
20 #include "chrome/browser/history/history_service_factory.h"
21 #include "chrome/browser/history/url_database.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "content/public/test/test_browser_thread_bundle.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using base::ASCIIToUTF16;
27
28 class BookmarkIndexTest : public testing::Test {
29  public:
30   BookmarkIndexTest() : model_(new BookmarkModel(NULL)) {}
31
32   void AddBookmarksWithTitles(const char** titles, size_t count) {
33     std::vector<std::string> title_vector;
34     for (size_t i = 0; i < count; ++i)
35       title_vector.push_back(titles[i]);
36     AddBookmarksWithTitles(title_vector);
37   }
38
39   void AddBookmarksWithTitles(const std::vector<std::string>& titles) {
40     GURL url("about:blank");
41     for (size_t i = 0; i < titles.size(); ++i)
42       model_->AddURL(model_->other_node(), static_cast<int>(i),
43                      ASCIIToUTF16(titles[i]), url);
44   }
45
46   void ExpectMatches(const std::string& query,
47                      const char** expected_titles,
48                      size_t expected_count) {
49     std::vector<std::string> title_vector;
50     for (size_t i = 0; i < expected_count; ++i)
51       title_vector.push_back(expected_titles[i]);
52     ExpectMatches(query, title_vector);
53   }
54
55   void ExpectMatches(const std::string& query,
56                      const std::vector<std::string>& expected_titles) {
57     std::vector<BookmarkTitleMatch> matches;
58     model_->GetBookmarksWithTitlesMatching(ASCIIToUTF16(query), 1000, &matches);
59     ASSERT_EQ(expected_titles.size(), matches.size());
60     for (size_t i = 0; i < expected_titles.size(); ++i) {
61       bool found = false;
62       for (size_t j = 0; j < matches.size(); ++j) {
63         if (ASCIIToUTF16(expected_titles[i]) == matches[j].node->GetTitle()) {
64           matches.erase(matches.begin() + j);
65           found = true;
66           break;
67         }
68       }
69       ASSERT_TRUE(found);
70     }
71   }
72
73   void ExtractMatchPositions(const std::string& string,
74                              BookmarkTitleMatch::MatchPositions* matches) {
75     std::vector<std::string> match_strings;
76     base::SplitString(string, ':', &match_strings);
77     for (size_t i = 0; i < match_strings.size(); ++i) {
78       std::vector<std::string> chunks;
79       base::SplitString(match_strings[i], ',', &chunks);
80       ASSERT_EQ(2U, chunks.size());
81       matches->push_back(BookmarkTitleMatch::MatchPosition());
82       int chunks0, chunks1;
83       base::StringToInt(chunks[0], &chunks0);
84       base::StringToInt(chunks[1], &chunks1);
85       matches->back().first = chunks0;
86       matches->back().second = chunks1;
87     }
88   }
89
90   void ExpectMatchPositions(
91       const std::string& query,
92       const BookmarkTitleMatch::MatchPositions& expected_positions) {
93     std::vector<BookmarkTitleMatch> matches;
94     model_->GetBookmarksWithTitlesMatching(ASCIIToUTF16(query), 1000, &matches);
95     ASSERT_EQ(1U, matches.size());
96     const BookmarkTitleMatch& match = matches[0];
97     ASSERT_EQ(expected_positions.size(), match.match_positions.size());
98     for (size_t i = 0; i < expected_positions.size(); ++i) {
99       EXPECT_EQ(expected_positions[i].first, match.match_positions[i].first);
100       EXPECT_EQ(expected_positions[i].second, match.match_positions[i].second);
101     }
102   }
103
104  protected:
105   scoped_ptr<BookmarkModel> model_;
106
107  private:
108   DISALLOW_COPY_AND_ASSIGN(BookmarkIndexTest);
109 };
110
111 // Various permutations with differing input, queries and output that exercises
112 // all query paths.
113 TEST_F(BookmarkIndexTest, Tests) {
114   struct TestData {
115     const std::string input;
116     const std::string query;
117     const std::string expected;
118   } data[] = {
119     // Trivial test case of only one term, exact match.
120     { "a;b",                        "A",        "a" },
121
122     // Prefix match, one term.
123     { "abcd;abc;b",                 "abc",      "abcd;abc" },
124
125     // Prefix match, multiple terms.
126     { "abcd cdef;abcd;abcd cdefg",  "abc cde",  "abcd cdef;abcd cdefg"},
127
128     // Exact and prefix match.
129     { "ab cdef;abcd;abcd cdefg",    "ab cdef",  "ab cdef"},
130
131     // Exact and prefix match.
132     { "ab cdef ghij;ab;cde;cdef;ghi;cdef ab;ghij ab",
133       "ab cde ghi",
134       "ab cdef ghij"},
135
136     // Title with term multiple times.
137     { "ab ab",                      "ab",       "ab ab"},
138
139     // Make sure quotes don't do a prefix match.
140     { "think",                      "\"thi\"",  ""},
141
142     // Prefix matches against multiple candidates.
143     { "abc1 abc2 abc3 abc4", "abc", "abc1 abc2 abc3 abc4"},
144   };
145   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
146     std::vector<std::string> titles;
147     base::SplitString(data[i].input, ';', &titles);
148     AddBookmarksWithTitles(titles);
149
150     std::vector<std::string> expected;
151     if (!data[i].expected.empty())
152       base::SplitString(data[i].expected, ';', &expected);
153
154     ExpectMatches(data[i].query, expected);
155
156     model_.reset(new BookmarkModel(NULL));
157   }
158 }
159
160 // Makes sure match positions are updated appropriately.
161 TEST_F(BookmarkIndexTest, MatchPositions) {
162   struct TestData {
163     const std::string title;
164     const std::string query;
165     const std::string expected;
166   } data[] = {
167     // Trivial test case of only one term, exact match.
168     { "a",                        "A",        "0,1" },
169     { "foo bar",                  "bar",      "4,7" },
170     { "fooey bark",               "bar foo",  "0,3:6,9"},
171     // Non-trivial tests.
172     { "foobar foo",               "foobar foo",   "0,6:7,10" },
173     { "foobar foo",               "foo foobar",   "0,6:7,10" },
174     { "foobar foobar",            "foobar foo",   "0,6:7,13" },
175     { "foobar foobar",            "foo foobar",   "0,6:7,13" },
176   };
177   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
178     std::vector<std::string> titles;
179     titles.push_back(data[i].title);
180     AddBookmarksWithTitles(titles);
181
182     BookmarkTitleMatch::MatchPositions expected_matches;
183     ExtractMatchPositions(data[i].expected, &expected_matches);
184     ExpectMatchPositions(data[i].query, expected_matches);
185
186     model_.reset(new BookmarkModel(NULL));
187   }
188 }
189
190 // Makes sure index is updated when a node is removed.
191 TEST_F(BookmarkIndexTest, Remove) {
192   const char* input[] = { "a", "b" };
193   AddBookmarksWithTitles(input, ARRAYSIZE_UNSAFE(input));
194
195   // Remove the node and make sure we don't get back any results.
196   model_->Remove(model_->other_node(), 0);
197   ExpectMatches("A", NULL, 0U);
198 }
199
200 // Makes sure index is updated when a node's title is changed.
201 TEST_F(BookmarkIndexTest, ChangeTitle) {
202   const char* input[] = { "a", "b" };
203   AddBookmarksWithTitles(input, ARRAYSIZE_UNSAFE(input));
204
205   // Remove the node and make sure we don't get back any results.
206   const char* expected[] = { "blah" };
207   model_->SetTitle(model_->other_node()->GetChild(0), ASCIIToUTF16("blah"));
208   ExpectMatches("BlAh", expected, ARRAYSIZE_UNSAFE(expected));
209 }
210
211 // Makes sure no more than max queries is returned.
212 TEST_F(BookmarkIndexTest, HonorMax) {
213   const char* input[] = { "abcd", "abcde" };
214   AddBookmarksWithTitles(input, ARRAYSIZE_UNSAFE(input));
215
216   std::vector<BookmarkTitleMatch> matches;
217   model_->GetBookmarksWithTitlesMatching(ASCIIToUTF16("ABc"), 1, &matches);
218   EXPECT_EQ(1U, matches.size());
219 }
220
221 // Makes sure if the lower case string of a bookmark title is more characters
222 // than the upper case string no match positions are returned.
223 TEST_F(BookmarkIndexTest, EmptyMatchOnMultiwideLowercaseString) {
224   const BookmarkNode* n1 = model_->AddURL(model_->other_node(), 0,
225                                           base::WideToUTF16(L"\u0130 i"),
226                                           GURL("http://www.google.com"));
227
228   std::vector<BookmarkTitleMatch> matches;
229   model_->GetBookmarksWithTitlesMatching(ASCIIToUTF16("i"), 100, &matches);
230   ASSERT_EQ(1U, matches.size());
231   EXPECT_TRUE(matches[0].node == n1);
232   EXPECT_TRUE(matches[0].match_positions.empty());
233 }
234
235 TEST_F(BookmarkIndexTest, GetResultsSortedByTypedCount) {
236   // This ensures MessageLoop::current() will exist, which is needed by
237   // TestingProfile::BlockUntilHistoryProcessesPendingRequests().
238   content::TestBrowserThreadBundle thread_bundle;
239
240   TestingProfile profile;
241   ASSERT_TRUE(profile.CreateHistoryService(true, false));
242   profile.BlockUntilHistoryProcessesPendingRequests();
243   profile.CreateBookmarkModel(true);
244
245   BookmarkModel* model = BookmarkModelFactory::GetForProfile(&profile);
246   test::WaitForBookmarkModelToLoad(model);
247
248   HistoryService* const history_service =
249       HistoryServiceFactory::GetForProfile(&profile, Profile::EXPLICIT_ACCESS);
250
251   history::URLDatabase* url_db = history_service->InMemoryDatabase();
252
253   struct TestData {
254     const GURL url;
255     const char* title;
256     const int typed_count;
257   } data[] = {
258     { GURL("http://www.google.com/"),      "Google",           100 },
259     { GURL("http://maps.google.com/"),     "Google Maps",       40 },
260     { GURL("http://docs.google.com/"),     "Google Docs",       50 },
261     { GURL("http://reader.google.com/"),   "Google Reader",     80 },
262   };
263
264   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
265     history::URLRow info(data[i].url);
266     info.set_title(base::UTF8ToUTF16(data[i].title));
267     info.set_typed_count(data[i].typed_count);
268     // Populate the InMemoryDatabase....
269     url_db->AddURL(info);
270     // Populate the BookmarkIndex.
271     model->AddURL(model->other_node(), i, base::UTF8ToUTF16(data[i].title),
272                   data[i].url);
273   }
274
275   // Check that the InMemoryDatabase stored the URLs properly.
276   history::URLRow result1;
277   url_db->GetRowForURL(data[0].url, &result1);
278   EXPECT_EQ(data[0].title, base::UTF16ToUTF8(result1.title()));
279
280   history::URLRow result2;
281   url_db->GetRowForURL(data[1].url, &result2);
282   EXPECT_EQ(data[1].title, base::UTF16ToUTF8(result2.title()));
283
284   history::URLRow result3;
285   url_db->GetRowForURL(data[2].url, &result3);
286   EXPECT_EQ(data[2].title, base::UTF16ToUTF8(result3.title()));
287
288   history::URLRow result4;
289   url_db->GetRowForURL(data[3].url, &result4);
290   EXPECT_EQ(data[3].title, base::UTF16ToUTF8(result4.title()));
291
292   // Populate match nodes.
293   std::vector<BookmarkTitleMatch> matches;
294   model->GetBookmarksWithTitlesMatching(ASCIIToUTF16("google"), 4, &matches);
295
296   // The resulting order should be:
297   // 1. Google (google.com) 100
298   // 2. Google Reader (google.com/reader) 80
299   // 3. Google Docs (docs.google.com) 50
300   // 4. Google Maps (maps.google.com) 40
301   EXPECT_EQ(4, static_cast<int>(matches.size()));
302   EXPECT_EQ(data[0].url, matches[0].node->url());
303   EXPECT_EQ(data[3].url, matches[1].node->url());
304   EXPECT_EQ(data[2].url, matches[2].node->url());
305   EXPECT_EQ(data[1].url, matches[3].node->url());
306
307   matches.clear();
308   // Select top two matches.
309   model->GetBookmarksWithTitlesMatching(ASCIIToUTF16("google"), 2, &matches);
310
311   EXPECT_EQ(2, static_cast<int>(matches.size()));
312   EXPECT_EQ(data[0].url, matches[0].node->url());
313   EXPECT_EQ(data[3].url, matches[1].node->url());
314 }