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/message_loop.h"
      6 #include "base/utf_string_conversions.h"
      7 #include "chrome/browser/autocomplete/autocomplete_match.h"
      8 #include "chrome/browser/autocomplete/keyword_provider.h"
      9 #include "chrome/browser/search_engines/template_url.h"
     10 #include "chrome/browser/search_engines/template_url_model.h"
     11 #include "chrome/test/testing_browser_process.h"
     12 #include "chrome/test/testing_browser_process_test.h"
     13 #include "googleurl/src/gurl.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 class KeywordProviderTest : public TestingBrowserProcessTest {
     17  protected:
     18   template<class ResultType>
     19   struct test_data {
     20     const string16 input;
     21     const size_t num_results;
     22     const ResultType output[3];
     23   };
     24 
     25   KeywordProviderTest() : kw_provider_(NULL) { }
     26   virtual ~KeywordProviderTest() { }
     27 
     28   virtual void SetUp();
     29   virtual void TearDown();
     30 
     31   template<class ResultType>
     32   void RunTest(test_data<ResultType>* keyword_cases,
     33                int num_cases,
     34                ResultType AutocompleteMatch::* member);
     35 
     36  protected:
     37   scoped_refptr<KeywordProvider> kw_provider_;
     38   scoped_ptr<TemplateURLModel> model_;
     39 };
     40 
     41 void KeywordProviderTest::SetUp() {
     42   static const TemplateURLModel::Initializer kTestKeywordData[] = {
     43     { "aa", "aa.com?foo=%s", "aa" },
     44     { "aaaa", "http://aaaa/?aaaa=1&b=%s&c", "aaaa" },
     45     { "aaaaa", "%s", "aaaaa" },
     46     { "ab", "bogus URL %s", "ab" },
     47     { "weasel", "weasel%sweasel", "weasel" },
     48     { "www", " +%2B?=%sfoo ", "www" },
     49     { "z", "%s=z", "z" },
     50   };
     51 
     52   model_.reset(new TemplateURLModel(kTestKeywordData,
     53                                     arraysize(kTestKeywordData)));
     54   kw_provider_ = new KeywordProvider(NULL, model_.get());
     55 }
     56 
     57 void KeywordProviderTest::TearDown() {
     58   model_.reset();
     59   kw_provider_ = NULL;
     60 }
     61 
     62 template<class ResultType>
     63 void KeywordProviderTest::RunTest(
     64     test_data<ResultType>* keyword_cases,
     65     int num_cases,
     66     ResultType AutocompleteMatch::* member) {
     67   ACMatches matches;
     68   for (int i = 0; i < num_cases; ++i) {
     69     AutocompleteInput input(keyword_cases[i].input, string16(), true,
     70                             false, true, AutocompleteInput::ALL_MATCHES);
     71     kw_provider_->Start(input, false);
     72     EXPECT_TRUE(kw_provider_->done());
     73     matches = kw_provider_->matches();
     74     EXPECT_EQ(keyword_cases[i].num_results, matches.size()) <<
     75                 ASCIIToUTF16("Input was: ") + keyword_cases[i].input;
     76     if (matches.size() == keyword_cases[i].num_results) {
     77       for (size_t j = 0; j < keyword_cases[i].num_results; ++j) {
     78         EXPECT_EQ(keyword_cases[i].output[j], matches[j].*member);
     79       }
     80     }
     81   }
     82 }
     83 
     84 TEST_F(KeywordProviderTest, Edit) {
     85   test_data<string16> edit_cases[] = {
     86     // Searching for a nonexistent prefix should give nothing.
     87     {ASCIIToUTF16("Not Found"),       0, {}},
     88     {ASCIIToUTF16("aaaaaNot Found"),  0, {}},
     89 
     90     // Check that tokenization only collapses whitespace between first tokens,
     91     // no-query-input cases have a space appended, and action is not escaped.
     92     {ASCIIToUTF16("z foo"),           1, {ASCIIToUTF16("z foo")}},
     93     {ASCIIToUTF16("z"),               1, {ASCIIToUTF16("z ")}},
     94     {ASCIIToUTF16("z    \t"),         1, {ASCIIToUTF16("z ")}},
     95     {ASCIIToUTF16("z   a   b   c++"), 1, {ASCIIToUTF16("z a   b   c++")}},
     96 
     97     // Matches should be limited to three, and sorted in quality order, not
     98     // alphabetical.
     99     {ASCIIToUTF16("aaa"),             2, {ASCIIToUTF16("aaaa "),
    100                                           ASCIIToUTF16("aaaaa ")}},
    101     {ASCIIToUTF16("a 1 2 3"),         3, {ASCIIToUTF16("aa 1 2 3"),
    102                                           ASCIIToUTF16("ab 1 2 3"),
    103                                           ASCIIToUTF16("aaaa 1 2 3")}},
    104     {ASCIIToUTF16("www.a"),           3, {ASCIIToUTF16("aa "),
    105                                           ASCIIToUTF16("ab "),
    106                                           ASCIIToUTF16("aaaa ")}},
    107     // Exact matches should prevent returning inexact matches.
    108     {ASCIIToUTF16("aaaa foo"),        1, {ASCIIToUTF16("aaaa foo")}},
    109     {ASCIIToUTF16("www.aaaa foo"),    1, {ASCIIToUTF16("aaaa foo")}},
    110 
    111     // Clean up keyword input properly.  "http" and "https" are the only
    112     // allowed schemes.
    113     {ASCIIToUTF16("www"),             1, {ASCIIToUTF16("www ")}},
    114     {ASCIIToUTF16("www."),            0, {}},
    115     {ASCIIToUTF16("www.w w"),         2, {ASCIIToUTF16("www w"),
    116                                           ASCIIToUTF16("weasel w")}},
    117     {ASCIIToUTF16("http://www"),      1, {ASCIIToUTF16("www ")}},
    118     {ASCIIToUTF16("http://www."),     0, {}},
    119     {ASCIIToUTF16("ftp: blah"),       0, {}},
    120     {ASCIIToUTF16("mailto:z"),        0, {}},
    121     {ASCIIToUTF16("ftp://z"),         0, {}},
    122     {ASCIIToUTF16("https://z"),       1, {ASCIIToUTF16("z ")}},
    123   };
    124 
    125   RunTest<string16>(edit_cases, arraysize(edit_cases),
    126                     &AutocompleteMatch::fill_into_edit);
    127 }
    128 
    129 TEST_F(KeywordProviderTest, URL) {
    130   test_data<GURL> url_cases[] = {
    131     // No query input -> empty destination URL.
    132     {ASCIIToUTF16("z"),               1, {GURL()}},
    133     {ASCIIToUTF16("z    \t"),         1, {GURL()}},
    134 
    135     // Check that tokenization only collapses whitespace between first tokens
    136     // and query input, but not rest of URL, is escaped.
    137     {ASCIIToUTF16("z   a   b   c++"), 1, {GURL("a+++b+++c%2B%2B=z")}},
    138     {ASCIIToUTF16("www.www www"),     1, {GURL(" +%2B?=wwwfoo ")}},
    139 
    140     // Substitution should work with various locations of the "%s".
    141     {ASCIIToUTF16("aaa 1a2b"),        2, {GURL("http://aaaa/?aaaa=1&b=1a2b&c"),
    142                                           GURL("1a2b")}},
    143     {ASCIIToUTF16("a 1 2 3"),         3, {GURL("aa.com?foo=1+2+3"),
    144                                           GURL("bogus URL 1+2+3"),
    145                                         GURL("http://aaaa/?aaaa=1&b=1+2+3&c")}},
    146     {ASCIIToUTF16("www.w w"),         2, {GURL(" +%2B?=wfoo "),
    147                                           GURL("weaselwweasel")}},
    148   };
    149 
    150   RunTest<GURL>(url_cases, arraysize(url_cases),
    151                 &AutocompleteMatch::destination_url);
    152 }
    153 
    154 TEST_F(KeywordProviderTest, Contents) {
    155   test_data<string16> contents_cases[] = {
    156     // No query input -> substitute "<enter query>" into contents.
    157     {ASCIIToUTF16("z"),               1,
    158         {ASCIIToUTF16("Search z for <enter query>")}},
    159     {ASCIIToUTF16("z    \t"),         1,
    160         {ASCIIToUTF16("Search z for <enter query>")}},
    161 
    162     // Check that tokenization only collapses whitespace between first tokens
    163     // and contents are not escaped or unescaped.
    164     {ASCIIToUTF16("z   a   b   c++"), 1,
    165         {ASCIIToUTF16("Search z for a   b   c++")}},
    166     {ASCIIToUTF16("www.www www"),     1, {ASCIIToUTF16("Search www for www")}},
    167 
    168     // Substitution should work with various locations of the "%s".
    169     {ASCIIToUTF16("aaa"),             2,
    170         {ASCIIToUTF16("Search aaaa for <enter query>"),
    171          ASCIIToUTF16("Search aaaaa for <enter query>")}},
    172     {ASCIIToUTF16("a 1 2 3"),         3, {ASCIIToUTF16("Search aa for 1 2 3"),
    173                                           ASCIIToUTF16("Search ab for 1 2 3"),
    174                                         ASCIIToUTF16("Search aaaa for 1 2 3")}},
    175     {ASCIIToUTF16("www.w w"),         2, {ASCIIToUTF16("Search www for w"),
    176         ASCIIToUTF16("Search weasel for w")}},
    177   };
    178 
    179   RunTest<string16>(contents_cases, arraysize(contents_cases),
    180                         &AutocompleteMatch::contents);
    181 }
    182 
    183 TEST_F(KeywordProviderTest, Description) {
    184   test_data<string16> description_cases[] = {
    185     // Whole keyword should be returned for both exact and inexact matches.
    186     {ASCIIToUTF16("z foo"),           1, {ASCIIToUTF16("(Keyword: z)")}},
    187     {ASCIIToUTF16("a foo"),           3, {ASCIIToUTF16("(Keyword: aa)"),
    188                                           ASCIIToUTF16("(Keyword: ab)"),
    189                                           ASCIIToUTF16("(Keyword: aaaa)")}},
    190     {ASCIIToUTF16("ftp://www.www w"), 0, {}},
    191     {ASCIIToUTF16("http://www.ab w"), 1, {ASCIIToUTF16("(Keyword: ab)")}},
    192 
    193     // Keyword should be returned regardless of query input.
    194     {ASCIIToUTF16("z"),               1, {ASCIIToUTF16("(Keyword: z)")}},
    195     {ASCIIToUTF16("z    \t"),         1, {ASCIIToUTF16("(Keyword: z)")}},
    196     {ASCIIToUTF16("z   a   b   c++"), 1, {ASCIIToUTF16("(Keyword: z)")}},
    197   };
    198 
    199   RunTest<string16>(description_cases, arraysize(description_cases),
    200                         &AutocompleteMatch::description);
    201 }
    202 
    203 TEST_F(KeywordProviderTest, AddKeyword) {
    204   TemplateURL* template_url = new TemplateURL();
    205   string16 keyword(ASCIIToUTF16("foo"));
    206   std::string url("http://www.google.com/foo?q={searchTerms}");
    207   template_url->SetURL(url, 0, 0);
    208   template_url->set_keyword(keyword);
    209   template_url->set_short_name(ASCIIToUTF16("Test"));
    210   model_->Add(template_url);
    211   ASSERT_TRUE(template_url == model_->GetTemplateURLForKeyword(keyword));
    212 }
    213 
    214 TEST_F(KeywordProviderTest, RemoveKeyword) {
    215   string16 url(ASCIIToUTF16("http://aaaa/?aaaa=1&b={searchTerms}&c"));
    216   model_->Remove(model_->GetTemplateURLForKeyword(ASCIIToUTF16("aaaa")));
    217   ASSERT_TRUE(model_->GetTemplateURLForKeyword(ASCIIToUTF16("aaaa")) == NULL);
    218 }
    219