history_contents_provider_unittest.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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/file_util.h"
6#include "base/path_service.h"
7#include "base/string_util.h"
8#include "base/utf_string_conversions.h"
9#include "chrome/browser/autocomplete/autocomplete.h"
10#include "chrome/browser/autocomplete/autocomplete_match.h"
11#include "chrome/browser/autocomplete/history_contents_provider.h"
12#include "chrome/browser/bookmarks/bookmark_model.h"
13#include "chrome/browser/browser_thread.h"
14#include "chrome/browser/history/history.h"
15#include "chrome/test/testing_profile.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using base::Time;
19using base::TimeDelta;
20
21namespace {
22
23struct TestEntry {
24  const char* url;
25  const char* title;
26  const char* body;
27} test_entries[] = {
28  {"http://www.google.com/1", "PAGEONE 1",   "FOO some body text"},
29  {"http://www.google.com/2", "PAGEONE 2",   "FOO some more blah blah"},
30  {"http://www.google.com/3", "PAGETHREE 3", "BAR some hello world for you"},
31};
32
33class HistoryContentsProviderTest : public testing::Test,
34                                    public ACProviderListener {
35 public:
36  HistoryContentsProviderTest()
37      : ui_thread_(BrowserThread::UI, &message_loop_),
38        file_thread_(BrowserThread::FILE, &message_loop_) {}
39
40  void RunQuery(const AutocompleteInput& input,
41                bool minimal_changes) {
42    provider_->Start(input, minimal_changes);
43
44    // When we're waiting for asynchronous messages, we have to spin the message
45    // loop. This will be exited in the OnProviderUpdate function when complete.
46    if (!input.synchronous_only())
47      MessageLoop::current()->Run();
48  }
49
50  const ACMatches& matches() const { return provider_->matches(); }
51
52  TestingProfile* profile() const { return profile_.get(); }
53
54  HistoryContentsProvider* provider() const { return provider_.get(); }
55
56 private:
57  // testing::Test
58  virtual void SetUp() {
59    profile_.reset(new TestingProfile());
60    profile_->CreateHistoryService(false, false);
61
62    HistoryService* history_service =
63        profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
64
65    // Populate history.
66    for (size_t i = 0; i < arraysize(test_entries); i++) {
67      // We need the ID scope and page ID so that the visit tracker can find it.
68      // We just use the index for the page ID below.
69      const void* id_scope = reinterpret_cast<void*>(1);
70      GURL url(test_entries[i].url);
71
72      // Add everything in order of time. We don't want to have a time that
73      // is "right now" or it will nondeterministically appear in the results.
74      Time t = Time::Now() - TimeDelta::FromDays(arraysize(test_entries) + i);
75
76      history_service->AddPage(url, t, id_scope, i, GURL(),
77                               PageTransition::LINK, history::RedirectList(),
78                               history::SOURCE_BROWSED, false);
79      history_service->SetPageTitle(url, UTF8ToUTF16(test_entries[i].title));
80      history_service->SetPageContents(url, UTF8ToUTF16(test_entries[i].body));
81    }
82
83    provider_ = new HistoryContentsProvider(this, profile_.get());
84  }
85
86  virtual void TearDown() {
87    provider_ = NULL;
88    profile_.reset(NULL);
89  }
90
91  // ACProviderListener
92  virtual void OnProviderUpdate(bool updated_matches) {
93    // When we quit, the test will get back control.
94    MessageLoop::current()->Quit();
95  }
96
97  MessageLoopForUI message_loop_;
98  BrowserThread ui_thread_;
99  BrowserThread file_thread_;
100
101  std::wstring history_dir_;
102
103  scoped_ptr<TestingProfile> profile_;
104  scoped_refptr<HistoryContentsProvider> provider_;
105};
106
107TEST_F(HistoryContentsProviderTest, Body) {
108  AutocompleteInput input(L"FOO", std::wstring(), true, false, true, false);
109  RunQuery(input, false);
110
111  // The results should be the first two pages, in decreasing order.
112  const ACMatches& m = matches();
113  ASSERT_EQ(2U, m.size());
114  EXPECT_EQ(test_entries[0].url, m[0].destination_url.spec());
115  EXPECT_STREQ(test_entries[0].title, WideToUTF8(m[0].description).c_str());
116  EXPECT_EQ(test_entries[1].url, m[1].destination_url.spec());
117  EXPECT_STREQ(test_entries[1].title, WideToUTF8(m[1].description).c_str());
118}
119
120TEST_F(HistoryContentsProviderTest, Title) {
121  AutocompleteInput input(L"PAGEONE", std::wstring(), true, false, true, false);
122  RunQuery(input, false);
123
124  // The results should be the first two pages.
125  const ACMatches& m = matches();
126  ASSERT_EQ(2U, m.size());
127  EXPECT_EQ(test_entries[0].url, m[0].destination_url.spec());
128  EXPECT_STREQ(test_entries[0].title, WideToUTF8(m[0].description).c_str());
129  EXPECT_EQ(test_entries[1].url, m[1].destination_url.spec());
130  EXPECT_STREQ(test_entries[1].title, WideToUTF8(m[1].description).c_str());
131}
132
133// The "minimal changes" flag should mean that we don't re-query the DB.
134TEST_F(HistoryContentsProviderTest, MinimalChanges) {
135  // A minimal changes request when there have been no real queries should
136  // give us no results.
137  AutocompleteInput sync_input(L"PAGEONE", std::wstring(), true, false, true,
138                               true);
139  RunQuery(sync_input, true);
140  const ACMatches& m1 = matches();
141  EXPECT_EQ(0U, m1.size());
142
143  // Now do a "regular" query to get the results.
144  AutocompleteInput async_input(L"PAGEONE", std::wstring(), true, false, true,
145                                false);
146  RunQuery(async_input, false);
147  const ACMatches& m2 = matches();
148  EXPECT_EQ(2U, m2.size());
149
150  // Now do a minimal one where we want synchronous results, and the results
151  // should still be there.
152  RunQuery(sync_input, true);
153  const ACMatches& m3 = matches();
154  EXPECT_EQ(2U, m3.size());
155}
156
157// Tests that the BookmarkModel is queried correctly.
158TEST_F(HistoryContentsProviderTest, Bookmarks) {
159  profile()->CreateBookmarkModel(false);
160  profile()->BlockUntilBookmarkModelLoaded();
161
162  // Add a bookmark.
163  GURL bookmark_url("http://www.google.com/4");
164  profile()->GetBookmarkModel()->SetURLStarred(bookmark_url,
165                                               ASCIIToUTF16("bar"), true);
166
167  // Ask for synchronous. This should only get the bookmark.
168  AutocompleteInput sync_input(L"bar", std::wstring(), true, false, true, true);
169  RunQuery(sync_input, false);
170  const ACMatches& m1 = matches();
171  ASSERT_EQ(1U, m1.size());
172  EXPECT_EQ(bookmark_url, m1[0].destination_url);
173  EXPECT_EQ(L"bar", m1[0].description);
174  EXPECT_TRUE(m1[0].starred);
175
176  // Ask for async. We should get the bookmark immediately.
177  AutocompleteInput async_input(L"bar", std::wstring(), true, false, true,
178                                false);
179  provider()->Start(async_input, false);
180  const ACMatches& m2 = matches();
181  ASSERT_EQ(1U, m2.size());
182  EXPECT_EQ(bookmark_url, m2[0].destination_url);
183
184  // Run the message loop (needed for async history results).
185  MessageLoop::current()->Run();
186
187  // We should two urls now, bookmark_url and http://www.google.com/3.
188  const ACMatches& m3 = matches();
189  ASSERT_EQ(2U, m3.size());
190  if (bookmark_url == m3[0].destination_url) {
191    EXPECT_EQ("http://www.google.com/3", m3[1].destination_url.spec());
192  } else {
193    EXPECT_EQ(bookmark_url, m3[1].destination_url);
194    EXPECT_EQ("http://www.google.com/3", m3[0].destination_url.spec());
195  }
196}
197
198}  // namespace
199