Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / history_querying_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 "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/bind_helpers.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/path_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/task/cancelable_task_tracker.h"
15 #include "chrome/browser/history/history_service.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 using base::Time;
19 using base::TimeDelta;
20
21 // Tests the history service for querying functionality.
22
23 namespace history {
24
25 namespace {
26
27 struct TestEntry {
28   const char* url;
29   const char* title;
30   const int days_ago;
31   Time time;  // Filled by SetUp.
32 } test_entries[] = {
33   // This one is visited super long ago so it will be in a different database
34   // from the next appearance of it at the end.
35   {"http://example.com/", "Other", 180},
36
37   // These are deliberately added out of chronological order. The history
38   // service should sort them by visit time when returning query results.
39   // The correct index sort order is 4 2 3 1 7 6 5 0.
40   {"http://www.google.com/1", "Title PAGEONE FOO some text", 10},
41   {"http://www.google.com/3", "Title PAGETHREE BAR some hello world", 8},
42   {"http://www.google.com/2", "Title PAGETWO FOO some more blah blah blah", 9},
43
44   // A more recent visit of the first one.
45   {"http://example.com/", "Other", 6},
46
47   {"http://www.google.com/6", "Title I'm the second oldest", 13},
48   {"http://www.google.com/4", "Title four", 12},
49   {"http://www.google.com/5", "Title five", 11},
50 };
51
52 // Returns true if the nth result in the given results set matches. It will
53 // return false on a non-match or if there aren't enough results.
54 bool NthResultIs(const QueryResults& results,
55                  int n,  // Result index to check.
56                  int test_entry_index) {  // Index of test_entries to compare.
57   if (static_cast<int>(results.size()) <= n)
58     return false;
59
60   const URLResult& result = results[n];
61
62   // Check the visit time.
63   if (result.visit_time() != test_entries[test_entry_index].time)
64     return false;
65
66   // Now check the URL & title.
67   return result.url() == GURL(test_entries[test_entry_index].url) &&
68          result.title() ==
69              base::UTF8ToUTF16(test_entries[test_entry_index].title);
70 }
71
72 }  // namespace
73
74 class HistoryQueryTest : public testing::Test {
75  public:
76   HistoryQueryTest() : page_id_(0) {
77   }
78
79   // Acts like a synchronous call to history's QueryHistory.
80   void QueryHistory(const std::string& text_query,
81                     const QueryOptions& options,
82                     QueryResults* results) {
83     history_->QueryHistory(base::UTF8ToUTF16(text_query),
84                            options,
85                            base::Bind(&HistoryQueryTest::QueryHistoryComplete,
86                                       base::Unretained(this)),
87                            &tracker_);
88     // Will go until ...Complete calls Quit.
89     base::MessageLoop::current()->Run();
90     results->Swap(&last_query_results_);
91   }
92
93   // Test paging through results, with a fixed number of results per page.
94   // Defined here so code can be shared for the text search and the non-text
95   // seach versions.
96   void TestPaging(const std::string& query_text,
97                   const int* expected_results,
98                   int results_length) {
99     ASSERT_TRUE(history_.get());
100
101     QueryOptions options;
102     QueryResults results;
103
104     options.max_count = 1;
105     for (int i = 0; i < results_length; i++) {
106       SCOPED_TRACE(testing::Message() << "i = " << i);
107       QueryHistory(query_text, options, &results);
108       ASSERT_EQ(1U, results.size());
109       EXPECT_TRUE(NthResultIs(results, 0, expected_results[i]));
110       options.end_time = results.back().visit_time();
111     }
112     QueryHistory(query_text, options, &results);
113     EXPECT_EQ(0U, results.size());
114
115     // Try with a max_count > 1.
116     options.max_count = 2;
117     options.end_time = base::Time();
118     for (int i = 0; i < results_length / 2; i++) {
119       SCOPED_TRACE(testing::Message() << "i = " << i);
120       QueryHistory(query_text, options, &results);
121       ASSERT_EQ(2U, results.size());
122       EXPECT_TRUE(NthResultIs(results, 0, expected_results[i * 2]));
123       EXPECT_TRUE(NthResultIs(results, 1, expected_results[i * 2 + 1]));
124       options.end_time = results.back().visit_time();
125     }
126
127     // Add a couple of entries with duplicate timestamps. Use |query_text| as
128     // the title of both entries so that they match a text query.
129     TestEntry duplicates[] = {
130       { "http://www.google.com/x",  query_text.c_str(), 1, },
131       { "http://www.google.com/y",  query_text.c_str(), 1, }
132     };
133     AddEntryToHistory(duplicates[0]);
134     AddEntryToHistory(duplicates[1]);
135
136     // Make sure that paging proceeds even if there are duplicate timestamps.
137     options.end_time = base::Time();
138     do {
139       QueryHistory(query_text, options, &results);
140       ASSERT_NE(options.end_time, results.back().visit_time());
141       options.end_time = results.back().visit_time();
142     } while (!results.reached_beginning());
143   }
144
145  protected:
146   scoped_ptr<HistoryService> history_;
147
148   // Counter used to generate a unique ID for each page added to the history.
149   int32 page_id_;
150
151   void AddEntryToHistory(const TestEntry& entry) {
152     // We need the ID scope and page ID so that the visit tracker can find it.
153     ContextID context_id = reinterpret_cast<ContextID>(1);
154     GURL url(entry.url);
155
156     history_->AddPage(url, entry.time, context_id, page_id_++, GURL(),
157                       history::RedirectList(), ui::PAGE_TRANSITION_LINK,
158                       history::SOURCE_BROWSED, false);
159     history_->SetPageTitle(url, base::UTF8ToUTF16(entry.title));
160   }
161
162  private:
163   virtual void SetUp() {
164     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
165     history_dir_ = temp_dir_.path().AppendASCII("HistoryTest");
166     ASSERT_TRUE(base::CreateDirectory(history_dir_));
167
168     history_.reset(new HistoryService);
169     if (!history_->Init(history_dir_)) {
170       history_.reset();  // Tests should notice this NULL ptr & fail.
171       return;
172     }
173
174     // Fill the test data.
175     Time now = Time::Now().LocalMidnight();
176     for (size_t i = 0; i < arraysize(test_entries); i++) {
177       test_entries[i].time =
178           now - (test_entries[i].days_ago * TimeDelta::FromDays(1));
179       AddEntryToHistory(test_entries[i]);
180     }
181   }
182
183   virtual void TearDown() {
184     if (history_) {
185       history_->SetOnBackendDestroyTask(base::MessageLoop::QuitClosure());
186       history_->Cleanup();
187       history_.reset();
188       base::MessageLoop::current()->Run();  // Wait for the other thread.
189     }
190   }
191
192   void QueryHistoryComplete(QueryResults* results) {
193     results->Swap(&last_query_results_);
194     base::MessageLoop::current()->Quit();  // Will return out to QueryHistory.
195   }
196
197   base::ScopedTempDir temp_dir_;
198
199   base::MessageLoop message_loop_;
200
201   base::FilePath history_dir_;
202
203   base::CancelableTaskTracker tracker_;
204
205   // The QueryHistoryComplete callback will put the results here so QueryHistory
206   // can return them.
207   QueryResults last_query_results_;
208
209   DISALLOW_COPY_AND_ASSIGN(HistoryQueryTest);
210 };
211
212 TEST_F(HistoryQueryTest, Basic) {
213   ASSERT_TRUE(history_.get());
214
215   QueryOptions options;
216   QueryResults results;
217
218   // Test duplicate collapsing. 0 is an older duplicate of 4, and should not
219   // appear in the result set.
220   QueryHistory(std::string(), options, &results);
221   EXPECT_EQ(7U, results.size());
222
223   EXPECT_TRUE(NthResultIs(results, 0, 4));
224   EXPECT_TRUE(NthResultIs(results, 1, 2));
225   EXPECT_TRUE(NthResultIs(results, 2, 3));
226   EXPECT_TRUE(NthResultIs(results, 3, 1));
227   EXPECT_TRUE(NthResultIs(results, 4, 7));
228   EXPECT_TRUE(NthResultIs(results, 5, 6));
229   EXPECT_TRUE(NthResultIs(results, 6, 5));
230
231   // Next query a time range. The beginning should be inclusive, the ending
232   // should be exclusive.
233   options.begin_time = test_entries[3].time;
234   options.end_time = test_entries[2].time;
235   QueryHistory(std::string(), options, &results);
236   EXPECT_EQ(1U, results.size());
237   EXPECT_TRUE(NthResultIs(results, 0, 3));
238 }
239
240 // Tests max_count feature for basic (non-Full Text Search) queries.
241 TEST_F(HistoryQueryTest, BasicCount) {
242   ASSERT_TRUE(history_.get());
243
244   QueryOptions options;
245   QueryResults results;
246
247   // Query all time but with a limit on the number of entries. We should
248   // get the N most recent entries.
249   options.max_count = 2;
250   QueryHistory(std::string(), options, &results);
251   EXPECT_EQ(2U, results.size());
252   EXPECT_TRUE(NthResultIs(results, 0, 4));
253   EXPECT_TRUE(NthResultIs(results, 1, 2));
254 }
255
256 TEST_F(HistoryQueryTest, ReachedBeginning) {
257   ASSERT_TRUE(history_.get());
258
259   QueryOptions options;
260   QueryResults results;
261
262   QueryHistory(std::string(), options, &results);
263   EXPECT_TRUE(results.reached_beginning());
264   QueryHistory("some", options, &results);
265   EXPECT_TRUE(results.reached_beginning());
266
267   options.begin_time = test_entries[1].time;
268   QueryHistory(std::string(), options, &results);
269   EXPECT_FALSE(results.reached_beginning());
270   QueryHistory("some", options, &results);
271   EXPECT_FALSE(results.reached_beginning());
272
273   // Try |begin_time| just later than the oldest visit.
274   options.begin_time = test_entries[0].time + TimeDelta::FromMicroseconds(1);
275   QueryHistory(std::string(), options, &results);
276   EXPECT_FALSE(results.reached_beginning());
277   QueryHistory("some", options, &results);
278   EXPECT_FALSE(results.reached_beginning());
279
280   // Try |begin_time| equal to the oldest visit.
281   options.begin_time = test_entries[0].time;
282   QueryHistory(std::string(), options, &results);
283   EXPECT_TRUE(results.reached_beginning());
284   QueryHistory("some", options, &results);
285   EXPECT_TRUE(results.reached_beginning());
286
287   // Try |begin_time| just earlier than the oldest visit.
288   options.begin_time = test_entries[0].time - TimeDelta::FromMicroseconds(1);
289   QueryHistory(std::string(), options, &results);
290   EXPECT_TRUE(results.reached_beginning());
291   QueryHistory("some", options, &results);
292   EXPECT_TRUE(results.reached_beginning());
293
294   // Test with |max_count| specified.
295   options.max_count = 1;
296   QueryHistory(std::string(), options, &results);
297   EXPECT_FALSE(results.reached_beginning());
298   QueryHistory("some", options, &results);
299   EXPECT_FALSE(results.reached_beginning());
300
301   // Test with |max_count| greater than the number of results,
302   // and exactly equal to the number of results.
303   options.max_count = 100;
304   QueryHistory(std::string(), options, &results);
305   EXPECT_TRUE(results.reached_beginning());
306   options.max_count = results.size();
307   QueryHistory(std::string(), options, &results);
308   EXPECT_TRUE(results.reached_beginning());
309
310   options.max_count = 100;
311   QueryHistory("some", options, &results);
312   EXPECT_TRUE(results.reached_beginning());
313   options.max_count = results.size();
314   QueryHistory("some", options, &results);
315   EXPECT_TRUE(results.reached_beginning());
316 }
317
318 // This does most of the same tests above, but performs a text searches for a
319 // string that will match the pages in question. This will trigger a
320 // different code path.
321 TEST_F(HistoryQueryTest, TextSearch) {
322   ASSERT_TRUE(history_.get());
323
324   QueryOptions options;
325   QueryResults results;
326
327   // Query all of them to make sure they are there and in order. Note that
328   // this query will return the starred item twice since we requested all
329   // starred entries and no de-duping.
330   QueryHistory("some", options, &results);
331   EXPECT_EQ(3U, results.size());
332   EXPECT_TRUE(NthResultIs(results, 0, 2));
333   EXPECT_TRUE(NthResultIs(results, 1, 3));
334   EXPECT_TRUE(NthResultIs(results, 2, 1));
335
336   // Do a query that should only match one of them.
337   QueryHistory("PAGETWO", options, &results);
338   EXPECT_EQ(1U, results.size());
339   EXPECT_TRUE(NthResultIs(results, 0, 3));
340
341   // Next query a time range. The beginning should be inclusive, the ending
342   // should be exclusive.
343   options.begin_time = test_entries[1].time;
344   options.end_time = test_entries[3].time;
345   QueryHistory("some", options, &results);
346   EXPECT_EQ(1U, results.size());
347   EXPECT_TRUE(NthResultIs(results, 0, 1));
348 }
349
350 // Tests prefix searching for text search queries.
351 TEST_F(HistoryQueryTest, TextSearchPrefix) {
352   ASSERT_TRUE(history_.get());
353
354   QueryOptions options;
355   QueryResults results;
356
357   // Query with a prefix search.  Should return matches for "PAGETWO" and
358   // "PAGETHREE".
359   QueryHistory("PAGET", options, &results);
360   EXPECT_EQ(2U, results.size());
361   EXPECT_TRUE(NthResultIs(results, 0, 2));
362   EXPECT_TRUE(NthResultIs(results, 1, 3));
363 }
364
365 // Tests max_count feature for text search queries.
366 TEST_F(HistoryQueryTest, TextSearchCount) {
367   ASSERT_TRUE(history_.get());
368
369   QueryOptions options;
370   QueryResults results;
371
372   // Query all time but with a limit on the number of entries. We should
373   // get the N most recent entries.
374   options.max_count = 2;
375   QueryHistory("some", options, &results);
376   EXPECT_EQ(2U, results.size());
377   EXPECT_TRUE(NthResultIs(results, 0, 2));
378   EXPECT_TRUE(NthResultIs(results, 1, 3));
379
380   // Now query a subset of the pages and limit by N items. "FOO" should match
381   // the 2nd & 3rd pages, but we should only get the 3rd one because of the one
382   // page max restriction.
383   options.max_count = 1;
384   QueryHistory("FOO", options, &results);
385   EXPECT_EQ(1U, results.size());
386   EXPECT_TRUE(NthResultIs(results, 0, 3));
387 }
388
389 // Tests IDN text search by both ASCII and UTF.
390 TEST_F(HistoryQueryTest, TextSearchIDN) {
391   ASSERT_TRUE(history_.get());
392
393   QueryOptions options;
394   QueryResults results;
395
396   TestEntry entry = { "http://xn--d1abbgf6aiiy.xn--p1ai/",  "Nothing", 0, };
397   AddEntryToHistory(entry);
398
399   struct QueryEntry {
400     std::string query;
401     size_t results_size;
402   } queries[] = {
403     { "bad query", 0 },
404     { std::string("xn--d1abbgf6aiiy.xn--p1ai"), 1 },
405     { base::WideToUTF8(std::wstring(L"\u043f\u0440\u0435\u0437") +
406                        L"\u0438\u0434\u0435\u043d\u0442.\u0440\u0444"), 1, },
407   };
408
409   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(queries); ++i) {
410     QueryHistory(queries[i].query, options, &results);
411     EXPECT_EQ(queries[i].results_size, results.size());
412   }
413 }
414
415 // Test iterating over pages of results.
416 TEST_F(HistoryQueryTest, Paging) {
417   // Since results are fetched 1 and 2 at a time, entry #0 and #6 will not
418   // be de-duplicated.
419   int expected_results[] = { 4, 2, 3, 1, 7, 6, 5, 0 };
420   TestPaging(std::string(), expected_results, arraysize(expected_results));
421 }
422
423 TEST_F(HistoryQueryTest, TextSearchPaging) {
424   // Since results are fetched 1 and 2 at a time, entry #0 and #6 will not
425   // be de-duplicated. Entry #4 does not contain the text "title", so it
426   // shouldn't appear.
427   int expected_results[] = { 2, 3, 1, 7, 6, 5 };
428   TestPaging("title", expected_results, arraysize(expected_results));
429 }
430
431 }  // namespace history