Home | History | Annotate | Download | only in autocomplete
      1 // Copyright (c) 2011 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/history/history.h"
     14 #include "chrome/test/testing_browser_process.h"
     15 #include "chrome/test/testing_browser_process_test.h"
     16 #include "chrome/test/testing_profile.h"
     17 #include "content/browser/browser_thread.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 using base::Time;
     21 using base::TimeDelta;
     22 
     23 namespace {
     24 
     25 struct TestEntry {
     26   const char* url;
     27   const char* title;
     28   const char* body;
     29 } test_entries[] = {
     30   {"http://www.google.com/1", "PAGEONE 1",   "FOO some body text"},
     31   {"http://www.google.com/2", "PAGEONE 2",   "FOO some more blah blah"},
     32   {"http://www.google.com/3", "PAGETHREE 3", "BAR some hello world for you"},
     33 };
     34 
     35 class HistoryContentsProviderTest : public TestingBrowserProcessTest,
     36                                     public ACProviderListener {
     37  public:
     38   HistoryContentsProviderTest()
     39       : ui_thread_(BrowserThread::UI, &message_loop_),
     40         file_thread_(BrowserThread::FILE, &message_loop_) {}
     41 
     42   void RunQuery(const AutocompleteInput& input,
     43                 bool minimal_changes) {
     44     provider_->Start(input, minimal_changes);
     45 
     46     // When we're waiting for asynchronous messages, we have to spin the message
     47     // loop. This will be exited in the OnProviderUpdate function when complete.
     48     if (input.matches_requested() == AutocompleteInput::ALL_MATCHES)
     49       MessageLoop::current()->Run();
     50   }
     51 
     52   const ACMatches& matches() const { return provider_->matches(); }
     53 
     54   TestingProfile* profile() const { return profile_.get(); }
     55 
     56   HistoryContentsProvider* provider() const { return provider_.get(); }
     57 
     58  private:
     59   // testing::Test
     60   virtual void SetUp() {
     61     profile_.reset(new TestingProfile());
     62     profile_->CreateHistoryService(false, false);
     63 
     64     HistoryService* history_service =
     65         profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
     66 
     67     // Populate history.
     68     for (size_t i = 0; i < arraysize(test_entries); i++) {
     69       // We need the ID scope and page ID so that the visit tracker can find it.
     70       // We just use the index for the page ID below.
     71       const void* id_scope = reinterpret_cast<void*>(1);
     72       GURL url(test_entries[i].url);
     73 
     74       // Add everything in order of time. We don't want to have a time that
     75       // is "right now" or it will nondeterministically appear in the results.
     76       Time t = Time::Now() - TimeDelta::FromDays(arraysize(test_entries) + i);
     77 
     78       history_service->AddPage(url, t, id_scope, i, GURL(),
     79                                PageTransition::LINK, history::RedirectList(),
     80                                history::SOURCE_BROWSED, false);
     81       history_service->SetPageTitle(url, UTF8ToUTF16(test_entries[i].title));
     82       history_service->SetPageContents(url, UTF8ToUTF16(test_entries[i].body));
     83     }
     84 
     85     provider_ = new HistoryContentsProvider(this, profile_.get());
     86   }
     87 
     88   virtual void TearDown() {
     89     provider_ = NULL;
     90     profile_.reset(NULL);
     91   }
     92 
     93   // ACProviderListener
     94   virtual void OnProviderUpdate(bool updated_matches) {
     95     // We must quit the message loop (if running) to return control to the test.
     96     // Note, calling Quit() directly will checkfail if the loop isn't running,
     97     // so we post a task, which is safe for either case.
     98     MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
     99   }
    100 
    101   MessageLoopForUI message_loop_;
    102   BrowserThread ui_thread_;
    103   BrowserThread file_thread_;
    104 
    105   scoped_ptr<TestingProfile> profile_;
    106   scoped_refptr<HistoryContentsProvider> provider_;
    107 };
    108 
    109 TEST_F(HistoryContentsProviderTest, Body) {
    110   AutocompleteInput input(ASCIIToUTF16("FOO"), string16(), true, false, true,
    111                           AutocompleteInput::ALL_MATCHES);
    112   RunQuery(input, false);
    113 
    114   // The results should be the first two pages, in decreasing order.
    115   const ACMatches& m = matches();
    116   ASSERT_EQ(2U, m.size());
    117   EXPECT_EQ(test_entries[0].url, m[0].destination_url.spec());
    118   EXPECT_STREQ(test_entries[0].title, UTF16ToUTF8(m[0].description).c_str());
    119   EXPECT_EQ(test_entries[1].url, m[1].destination_url.spec());
    120   EXPECT_STREQ(test_entries[1].title, UTF16ToUTF8(m[1].description).c_str());
    121 }
    122 
    123 TEST_F(HistoryContentsProviderTest, Title) {
    124   AutocompleteInput input(ASCIIToUTF16("PAGEONE"), string16(), true, false,
    125                           true, AutocompleteInput::ALL_MATCHES);
    126   RunQuery(input, false);
    127 
    128   // The results should be the first two pages.
    129   const ACMatches& m = matches();
    130   ASSERT_EQ(2U, m.size());
    131   EXPECT_EQ(test_entries[0].url, m[0].destination_url.spec());
    132   EXPECT_STREQ(test_entries[0].title, UTF16ToUTF8(m[0].description).c_str());
    133   EXPECT_EQ(test_entries[1].url, m[1].destination_url.spec());
    134   EXPECT_STREQ(test_entries[1].title, UTF16ToUTF8(m[1].description).c_str());
    135 }
    136 
    137 // The "minimal changes" flag should mean that we don't re-query the DB.
    138 TEST_F(HistoryContentsProviderTest, MinimalChanges) {
    139   // A minimal changes request when there have been no real queries should
    140   // give us no results.
    141   AutocompleteInput sync_input(ASCIIToUTF16("PAGEONE"), string16(), true, false,
    142                                true, AutocompleteInput::SYNCHRONOUS_MATCHES);
    143   RunQuery(sync_input, true);
    144   const ACMatches& m1 = matches();
    145   EXPECT_EQ(0U, m1.size());
    146 
    147   // Now do a "regular" query to get the results.
    148   AutocompleteInput async_input(ASCIIToUTF16("PAGEONE"), string16(), true,
    149                                 false, true, AutocompleteInput::ALL_MATCHES);
    150   RunQuery(async_input, false);
    151   const ACMatches& m2 = matches();
    152   EXPECT_EQ(2U, m2.size());
    153 
    154   // Now do a minimal one where we want synchronous results, and the results
    155   // should still be there.
    156   RunQuery(sync_input, true);
    157   const ACMatches& m3 = matches();
    158   EXPECT_EQ(2U, m3.size());
    159 }
    160 
    161 // Tests that the BookmarkModel is queried correctly.
    162 TEST_F(HistoryContentsProviderTest, Bookmarks) {
    163   profile()->CreateBookmarkModel(false);
    164   profile()->BlockUntilBookmarkModelLoaded();
    165 
    166   // Add a bookmark.
    167   GURL bookmark_url("http://www.google.com/4");
    168   profile()->GetBookmarkModel()->SetURLStarred(bookmark_url,
    169                                                ASCIIToUTF16("bar"), true);
    170 
    171   // Ask for synchronous. This should only get the bookmark.
    172   AutocompleteInput sync_input(ASCIIToUTF16("bar"), string16(), true, false,
    173                                true, AutocompleteInput::SYNCHRONOUS_MATCHES);
    174   RunQuery(sync_input, false);
    175   const ACMatches& m1 = matches();
    176   ASSERT_EQ(1U, m1.size());
    177   EXPECT_EQ(bookmark_url, m1[0].destination_url);
    178   EXPECT_EQ(ASCIIToUTF16("bar"), m1[0].description);
    179   EXPECT_TRUE(m1[0].starred);
    180 
    181   // Ask for async. We should get the bookmark immediately.
    182   AutocompleteInput async_input(ASCIIToUTF16("bar"), string16(), true, false,
    183                                 true, AutocompleteInput::ALL_MATCHES);
    184   provider()->Start(async_input, false);
    185   const ACMatches& m2 = matches();
    186   ASSERT_EQ(1U, m2.size());
    187   EXPECT_EQ(bookmark_url, m2[0].destination_url);
    188 
    189   // Run the message loop (needed for async history results).
    190   MessageLoop::current()->Run();
    191 
    192   // We should have two urls now, bookmark_url and http://www.google.com/3.
    193   const ACMatches& m3 = matches();
    194   ASSERT_EQ(2U, m3.size());
    195   if (bookmark_url == m3[0].destination_url) {
    196     EXPECT_EQ("http://www.google.com/3", m3[1].destination_url.spec());
    197   } else {
    198     EXPECT_EQ(bookmark_url, m3[1].destination_url);
    199     EXPECT_EQ("http://www.google.com/3", m3[0].destination_url.spec());
    200   }
    201 }
    202 
    203 // Tests that history is deleted properly.
    204 TEST_F(HistoryContentsProviderTest, DeleteMatch) {
    205   AutocompleteInput input(ASCIIToUTF16("bar"), string16(), true, false, true,
    206                           AutocompleteInput::ALL_MATCHES);
    207   RunQuery(input, false);
    208 
    209   // Query; the result should be the third page.
    210   const ACMatches& m = matches();
    211   ASSERT_EQ(1U, m.size());
    212   EXPECT_EQ(test_entries[2].url, m[0].destination_url.spec());
    213 
    214   // Now delete the match and ensure it was removed.
    215   provider()->DeleteMatch(m[0]);
    216   EXPECT_EQ(0U, matches().size());
    217 }
    218 
    219 // Tests deleting starred results from history, not affecting bookmarks/matches.
    220 TEST_F(HistoryContentsProviderTest, DeleteStarredMatch) {
    221   profile()->CreateBookmarkModel(false);
    222   profile()->BlockUntilBookmarkModelLoaded();
    223 
    224   // Bookmark a history item.
    225   GURL bookmark_url(test_entries[2].url);
    226   profile()->GetBookmarkModel()->SetURLStarred(bookmark_url,
    227                                                ASCIIToUTF16("bar"), true);
    228 
    229   // Get the match to delete its history
    230   AutocompleteInput input(ASCIIToUTF16("bar"), string16(), true, false, true,
    231                           AutocompleteInput::ALL_MATCHES);
    232   RunQuery(input, false);
    233   const ACMatches& m = matches();
    234   ASSERT_EQ(1U, m.size());
    235 
    236   // Now delete the match and ensure it was *not* removed.
    237   provider()->DeleteMatch(m[0]);
    238   EXPECT_EQ(1U, matches().size());
    239 
    240   // Run a query that would only match history (but the history is deleted)
    241   AutocompleteInput you_input(ASCIIToUTF16("you"), string16(), true, false,
    242                               true, AutocompleteInput::ALL_MATCHES);
    243   RunQuery(you_input, false);
    244   EXPECT_EQ(0U, matches().size());
    245 
    246   // Run a query that matches the bookmark
    247   RunQuery(input, false);
    248   EXPECT_EQ(1U, matches().size());
    249 }
    250 
    251 }  // namespace
    252