Home | History | Annotate | Download | only in search_engines
      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/base_paths.h"
      6 #include "base/string_util.h"
      7 #include "base/utf_string_conversions.h"
      8 #include "chrome/browser/browser_process.h"
      9 #include "chrome/browser/rlz/rlz.h"
     10 #include "chrome/browser/search_engines/search_terms_data.h"
     11 #include "chrome/browser/search_engines/template_url.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 
     14 // Simple implementation of SearchTermsData.
     15 class TestSearchTermsData : public SearchTermsData {
     16  public:
     17   explicit TestSearchTermsData(const char* google_base_url)
     18       : google_base_url_(google_base_url)  {
     19   }
     20 
     21   virtual std::string GoogleBaseURLValue() const {
     22     return google_base_url_;
     23   }
     24 
     25   virtual std::string GetApplicationLocale() const {
     26     return "yy";
     27   }
     28 
     29 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
     30   // Returns the value for the Chrome Omnibox rlz.
     31   virtual string16 GetRlzParameterValue() const {
     32     return string16();
     33   }
     34 #endif
     35 
     36  private:
     37   std::string google_base_url_;
     38 
     39   DISALLOW_COPY_AND_ASSIGN(TestSearchTermsData);
     40 };
     41 
     42 class TemplateURLTest : public testing::Test {
     43  public:
     44   virtual void TearDown() {
     45     TemplateURLRef::SetGoogleBaseURL(NULL);
     46   }
     47 
     48   void CheckSuggestBaseURL(const char* base_url,
     49                            const char* base_suggest_url) const {
     50     TestSearchTermsData search_terms_data(base_url);
     51     EXPECT_STREQ(base_suggest_url,
     52                  search_terms_data.GoogleBaseSuggestURLValue().c_str());
     53   }
     54 };
     55 
     56 TEST_F(TemplateURLTest, Defaults) {
     57   TemplateURL url;
     58   ASSERT_FALSE(url.show_in_default_list());
     59   ASSERT_FALSE(url.safe_for_autoreplace());
     60   ASSERT_EQ(0, url.prepopulate_id());
     61 }
     62 
     63 TEST_F(TemplateURLTest, TestValidWithComplete) {
     64   TemplateURLRef ref("{searchTerms}", 0, 0);
     65   ASSERT_TRUE(ref.IsValid());
     66 }
     67 
     68 TEST_F(TemplateURLTest, URLRefTestSearchTerms) {
     69   struct SearchTermsCase {
     70     const char* url;
     71     const string16 terms;
     72     const char* output;
     73   } search_term_cases[] = {
     74     { "http://foo{searchTerms}", ASCIIToUTF16("sea rch/bar"),
     75       "http://foosea%20rch/bar" },
     76     { "http://foo{searchTerms}?boo=abc", ASCIIToUTF16("sea rch/bar"),
     77       "http://foosea%20rch/bar?boo=abc" },
     78     { "http://foo/?boo={searchTerms}", ASCIIToUTF16("sea rch/bar"),
     79       "http://foo/?boo=sea+rch%2Fbar" },
     80     { "http://en.wikipedia.org/{searchTerms}", ASCIIToUTF16("wiki/?"),
     81       "http://en.wikipedia.org/wiki/%3F" }
     82   };
     83   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(search_term_cases); ++i) {
     84     const SearchTermsCase& value = search_term_cases[i];
     85     TemplateURL t_url;
     86     TemplateURLRef ref(value.url, 0, 0);
     87     ASSERT_TRUE(ref.IsValid());
     88 
     89     ASSERT_TRUE(ref.SupportsReplacement());
     90     GURL result = GURL(ref.ReplaceSearchTerms(t_url, value.terms,
     91         TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
     92     ASSERT_TRUE(result.is_valid());
     93     ASSERT_EQ(value.output, result.spec());
     94   }
     95 }
     96 
     97 TEST_F(TemplateURLTest, URLRefTestCount) {
     98   TemplateURL t_url;
     99   TemplateURLRef ref("http://foo{searchTerms}{count?}", 0, 0);
    100   ASSERT_TRUE(ref.IsValid());
    101   ASSERT_TRUE(ref.SupportsReplacement());
    102   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    103       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    104   ASSERT_TRUE(result.is_valid());
    105   ASSERT_EQ("http://foox/", result.spec());
    106 }
    107 
    108 TEST_F(TemplateURLTest, URLRefTestCount2) {
    109   TemplateURL t_url;
    110   TemplateURLRef ref("http://foo{searchTerms}{count}", 0, 0);
    111   ASSERT_TRUE(ref.IsValid());
    112   ASSERT_TRUE(ref.SupportsReplacement());
    113   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    114       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    115   ASSERT_TRUE(result.is_valid());
    116   ASSERT_EQ("http://foox10/", result.spec());
    117 }
    118 
    119 TEST_F(TemplateURLTest, URLRefTestIndices) {
    120   TemplateURL t_url;
    121   TemplateURLRef ref("http://foo{searchTerms}x{startIndex?}y{startPage?}",
    122                      1, 2);
    123   ASSERT_TRUE(ref.IsValid());
    124   ASSERT_TRUE(ref.SupportsReplacement());
    125   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    126       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    127   ASSERT_TRUE(result.is_valid());
    128   ASSERT_EQ("http://fooxxy/", result.spec());
    129 }
    130 
    131 TEST_F(TemplateURLTest, URLRefTestIndices2) {
    132   TemplateURL t_url;
    133   TemplateURLRef ref("http://foo{searchTerms}x{startIndex}y{startPage}", 1, 2);
    134   ASSERT_TRUE(ref.IsValid());
    135   ASSERT_TRUE(ref.SupportsReplacement());
    136   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    137       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    138   ASSERT_TRUE(result.is_valid());
    139   ASSERT_EQ("http://fooxx1y2/", result.spec());
    140 }
    141 
    142 TEST_F(TemplateURLTest, URLRefTestEncoding) {
    143   TemplateURL t_url;
    144   TemplateURLRef ref(
    145       "http://foo{searchTerms}x{inputEncoding?}y{outputEncoding?}a", 1, 2);
    146   ASSERT_TRUE(ref.IsValid());
    147   ASSERT_TRUE(ref.SupportsReplacement());
    148   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    149       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    150   ASSERT_TRUE(result.is_valid());
    151   ASSERT_EQ("http://fooxxutf-8ya/", result.spec());
    152 }
    153 
    154 TEST_F(TemplateURLTest, InputEncodingBeforeSearchTerm) {
    155   TemplateURL t_url;
    156   TemplateURLRef ref(
    157       "http://foox{inputEncoding?}a{searchTerms}y{outputEncoding?}b", 1, 2);
    158   ASSERT_TRUE(ref.IsValid());
    159   ASSERT_TRUE(ref.SupportsReplacement());
    160   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    161       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    162   ASSERT_TRUE(result.is_valid());
    163   ASSERT_EQ("http://fooxutf-8axyb/", result.spec());
    164 }
    165 
    166 TEST_F(TemplateURLTest, URLRefTestEncoding2) {
    167   TemplateURL t_url;
    168   TemplateURLRef ref(
    169       "http://foo{searchTerms}x{inputEncoding}y{outputEncoding}a", 1, 2);
    170   ASSERT_TRUE(ref.IsValid());
    171   ASSERT_TRUE(ref.SupportsReplacement());
    172   GURL result = GURL(ref.ReplaceSearchTerms(t_url, ASCIIToUTF16("X"),
    173       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    174   ASSERT_TRUE(result.is_valid());
    175   ASSERT_EQ("http://fooxxutf-8yutf-8a/", result.spec());
    176 }
    177 
    178 TEST_F(TemplateURLTest, URLRefTestSearchTermsUsingTermsData) {
    179   struct SearchTermsCase {
    180     const char* url;
    181     const string16 terms;
    182     const char* output;
    183   } search_term_cases[] = {
    184     { "{google:baseURL}{language}{searchTerms}", string16(),
    185       "http://example.com/e/yy" },
    186     { "{google:baseSuggestURL}{searchTerms}", string16(),
    187       "http://clients1.example.com/complete/" }
    188   };
    189 
    190   TestSearchTermsData search_terms_data("http://example.com/e/");
    191   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(search_term_cases); ++i) {
    192     const SearchTermsCase& value = search_term_cases[i];
    193     TemplateURL t_url;
    194     TemplateURLRef ref(value.url, 0, 0);
    195     ASSERT_TRUE(ref.IsValid());
    196 
    197     ASSERT_TRUE(ref.SupportsReplacement());
    198     GURL result = GURL(ref.ReplaceSearchTermsUsingTermsData(
    199         t_url, value.terms,
    200         TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16(),
    201         search_terms_data));
    202     ASSERT_TRUE(result.is_valid());
    203     ASSERT_EQ(value.output, result.spec());
    204   }
    205 }
    206 
    207 TEST_F(TemplateURLTest, URLRefTermToWide) {
    208   struct ToWideCase {
    209     const char* encoded_search_term;
    210     const string16 expected_decoded_term;
    211   } to_wide_cases[] = {
    212     {"hello+world", ASCIIToUTF16("hello world")},
    213     // Test some big-5 input.
    214     {"%a7A%A6%6e+to+you", WideToUTF16(L"\x4f60\x597d to you")},
    215     // Test some UTF-8 input. We should fall back to this when the encoding
    216     // doesn't look like big-5. We have a '5' in the middle, which is an invalid
    217     // Big-5 trailing byte.
    218     {"%e4%bd%a05%e5%a5%bd+to+you", WideToUTF16(L"\x4f60\x35\x597d to you")},
    219     // Undecodable input should stay escaped.
    220     {"%91%01+abcd", WideToUTF16(L"%91%01 abcd")},
    221     // Make sure we convert %2B to +.
    222     {"C%2B%2B", ASCIIToUTF16("C++")},
    223     // C%2B is escaped as C%252B, make sure we unescape it properly.
    224     {"C%252B", ASCIIToUTF16("C%2B")},
    225   };
    226 
    227   TemplateURL t_url;
    228 
    229   // Set one input encoding: big-5. This is so we can test fallback to UTF-8.
    230   std::vector<std::string> encodings;
    231   encodings.push_back("big-5");
    232   t_url.set_input_encodings(encodings);
    233 
    234   TemplateURLRef ref("http://foo?q={searchTerms}", 1, 2);
    235   ASSERT_TRUE(ref.IsValid());
    236   ASSERT_TRUE(ref.SupportsReplacement());
    237 
    238   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(to_wide_cases); i++) {
    239     string16 result = ref.SearchTermToString16(t_url,
    240         to_wide_cases[i].encoded_search_term);
    241 
    242     EXPECT_EQ(to_wide_cases[i].expected_decoded_term, result);
    243   }
    244 }
    245 
    246 TEST_F(TemplateURLTest, SetFavicon) {
    247   TemplateURL url;
    248   GURL favicon_url("http://favicon.url");
    249   url.SetFaviconURL(favicon_url);
    250   ASSERT_EQ(1U, url.image_refs().size());
    251   ASSERT_TRUE(favicon_url == url.GetFaviconURL());
    252 
    253   GURL favicon_url2("http://favicon2.url");
    254   url.SetFaviconURL(favicon_url2);
    255   ASSERT_EQ(1U, url.image_refs().size());
    256   ASSERT_TRUE(favicon_url2 == url.GetFaviconURL());
    257 }
    258 
    259 TEST_F(TemplateURLTest, DisplayURLToURLRef) {
    260   struct TestData {
    261     const std::string url;
    262     const string16 expected_result;
    263   } data[] = {
    264     { "http://foo{searchTerms}x{inputEncoding}y{outputEncoding}a",
    265       ASCIIToUTF16("http://foo%sx{inputEncoding}y{outputEncoding}a") },
    266     { "http://X",
    267       ASCIIToUTF16("http://X") },
    268     { "http://foo{searchTerms",
    269       ASCIIToUTF16("http://foo{searchTerms") },
    270     { "http://foo{searchTerms}{language}",
    271       ASCIIToUTF16("http://foo%s{language}") },
    272   };
    273   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
    274     TemplateURLRef ref(data[i].url, 1, 2);
    275     EXPECT_EQ(data[i].expected_result, ref.DisplayURL());
    276     EXPECT_EQ(data[i].url,
    277               TemplateURLRef::DisplayURLToURLRef(ref.DisplayURL()));
    278   }
    279 }
    280 
    281 TEST_F(TemplateURLTest, ReplaceSearchTerms) {
    282   struct TestData {
    283     const std::string url;
    284     const std::string expected_result;
    285   } data[] = {
    286     { "http://foo/{language}{searchTerms}{inputEncoding}",
    287       "http://foo/{language}XUTF-8" },
    288     { "http://foo/{language}{inputEncoding}{searchTerms}",
    289       "http://foo/{language}UTF-8X" },
    290     { "http://foo/{searchTerms}{language}{inputEncoding}",
    291       "http://foo/X{language}UTF-8" },
    292     { "http://foo/{searchTerms}{inputEncoding}{language}",
    293       "http://foo/XUTF-8{language}" },
    294     { "http://foo/{inputEncoding}{searchTerms}{language}",
    295       "http://foo/UTF-8X{language}" },
    296     { "http://foo/{inputEncoding}{language}{searchTerms}",
    297       "http://foo/UTF-8{language}X" },
    298     { "http://foo/{language}a{searchTerms}a{inputEncoding}a",
    299       "http://foo/{language}aXaUTF-8a" },
    300     { "http://foo/{language}a{inputEncoding}a{searchTerms}a",
    301       "http://foo/{language}aUTF-8aXa" },
    302     { "http://foo/{searchTerms}a{language}a{inputEncoding}a",
    303       "http://foo/Xa{language}aUTF-8a" },
    304     { "http://foo/{searchTerms}a{inputEncoding}a{language}a",
    305       "http://foo/XaUTF-8a{language}a" },
    306     { "http://foo/{inputEncoding}a{searchTerms}a{language}a",
    307       "http://foo/UTF-8aXa{language}a" },
    308     { "http://foo/{inputEncoding}a{language}a{searchTerms}a",
    309       "http://foo/UTF-8a{language}aXa" },
    310   };
    311   TemplateURL turl;
    312   turl.add_input_encoding("UTF-8");
    313   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
    314     TemplateURLRef ref(data[i].url, 1, 2);
    315     EXPECT_TRUE(ref.IsValid());
    316     EXPECT_TRUE(ref.SupportsReplacement());
    317     std::string expected_result = data[i].expected_result;
    318     ReplaceSubstringsAfterOffset(&expected_result, 0, "{language}",
    319         g_browser_process->GetApplicationLocale());
    320     GURL result = GURL(ref.ReplaceSearchTerms(turl, ASCIIToUTF16("X"),
    321         TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    322     EXPECT_TRUE(result.is_valid());
    323     EXPECT_EQ(expected_result, result.spec());
    324   }
    325 }
    326 
    327 
    328 // Tests replacing search terms in various encodings and making sure the
    329 // generated URL matches the expected value.
    330 TEST_F(TemplateURLTest, ReplaceArbitrarySearchTerms) {
    331   struct TestData {
    332     const std::string encoding;
    333     const string16 search_term;
    334     const std::string url;
    335     const std::string expected_result;
    336   } data[] = {
    337     { "BIG5",  WideToUTF16(L"\x60BD"),
    338       "http://foo/?{searchTerms}{inputEncoding}",
    339       "http://foo/?%B1~BIG5" },
    340     { "UTF-8", ASCIIToUTF16("blah"),
    341       "http://foo/?{searchTerms}{inputEncoding}",
    342       "http://foo/?blahUTF-8" },
    343   };
    344   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
    345     TemplateURL turl;
    346     turl.add_input_encoding(data[i].encoding);
    347     TemplateURLRef ref(data[i].url, 1, 2);
    348     GURL result = GURL(ref.ReplaceSearchTerms(turl,
    349         data[i].search_term, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
    350         string16()));
    351     EXPECT_TRUE(result.is_valid());
    352     EXPECT_EQ(data[i].expected_result, result.spec());
    353   }
    354 }
    355 
    356 TEST_F(TemplateURLTest, Suggestions) {
    357   struct TestData {
    358     const int accepted_suggestion;
    359     const string16 original_query_for_suggestion;
    360     const std::string expected_result;
    361   } data[] = {
    362     { TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16(),
    363       "http://bar/foo?q=foobar" },
    364     { TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, ASCIIToUTF16("foo"),
    365       "http://bar/foo?q=foobar" },
    366     { TemplateURLRef::NO_SUGGESTION_CHOSEN, string16(),
    367       "http://bar/foo?aq=f&q=foobar" },
    368     { TemplateURLRef::NO_SUGGESTION_CHOSEN, ASCIIToUTF16("foo"),
    369       "http://bar/foo?aq=f&q=foobar" },
    370     { 0, string16(), "http://bar/foo?aq=0&oq=&q=foobar" },
    371     { 1, ASCIIToUTF16("foo"), "http://bar/foo?aq=1&oq=foo&q=foobar" },
    372   };
    373   TemplateURL turl;
    374   turl.add_input_encoding("UTF-8");
    375   TemplateURLRef ref("http://bar/foo?{google:acceptedSuggestion}"
    376       "{google:originalQueryForSuggestion}q={searchTerms}", 1, 2);
    377   ASSERT_TRUE(ref.IsValid());
    378   ASSERT_TRUE(ref.SupportsReplacement());
    379   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
    380     GURL result = GURL(ref.ReplaceSearchTerms(turl, ASCIIToUTF16("foobar"),
    381         data[i].accepted_suggestion, data[i].original_query_for_suggestion));
    382     EXPECT_TRUE(result.is_valid());
    383     EXPECT_EQ(data[i].expected_result, result.spec());
    384   }
    385 }
    386 
    387 #if defined(OS_WIN)
    388 TEST_F(TemplateURLTest, RLZ) {
    389   string16 rlz_string;
    390 #if defined(GOOGLE_CHROME_BUILD)
    391   RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz_string);
    392 #endif
    393 
    394   TemplateURL t_url;
    395   TemplateURLRef ref("http://bar/?{google:RLZ}{searchTerms}", 1, 2);
    396   ASSERT_TRUE(ref.IsValid());
    397   ASSERT_TRUE(ref.SupportsReplacement());
    398   GURL result(ref.ReplaceSearchTerms(t_url, L"x",
    399       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()));
    400   ASSERT_TRUE(result.is_valid());
    401   std::string expected_url = "http://bar/?";
    402   if (!rlz_string.empty()) {
    403     expected_url += "rlz=" + WideToUTF8(rlz_string) + "&";
    404   }
    405   expected_url += "x";
    406   ASSERT_EQ(expected_url, result.spec());
    407 }
    408 #endif
    409 
    410 TEST_F(TemplateURLTest, HostAndSearchTermKey) {
    411   struct TestData {
    412     const std::string url;
    413     const std::string host;
    414     const std::string path;
    415     const std::string search_term_key;
    416   } data[] = {
    417     { "http://blah/?foo=bar&q={searchTerms}&b=x", "blah", "/", "q"},
    418 
    419     // No query key should result in empty values.
    420     { "http://blah/{searchTerms}", "", "", ""},
    421 
    422     // No term should result in empty values.
    423     { "http://blah/", "", "", ""},
    424 
    425     // Multiple terms should result in empty values.
    426     { "http://blah/?q={searchTerms}&x={searchTerms}", "", "", ""},
    427 
    428     // Term in the host shouldn't match.
    429     { "http://{searchTerms}", "", "", ""},
    430 
    431     { "http://blah/?q={searchTerms}", "blah", "/", "q"},
    432 
    433     // Single term with extra chars in value should match.
    434     { "http://blah/?q=stock:{searchTerms}", "blah", "/", "q"},
    435   };
    436 
    437   TemplateURL t_url;
    438   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
    439     t_url.SetURL(data[i].url, 0, 0);
    440     EXPECT_EQ(data[i].host, t_url.url()->GetHost());
    441     EXPECT_EQ(data[i].path, t_url.url()->GetPath());
    442     EXPECT_EQ(data[i].search_term_key, t_url.url()->GetSearchTermKey());
    443   }
    444 }
    445 
    446 TEST_F(TemplateURLTest, GoogleBaseSuggestURL) {
    447   static const struct {
    448     const char* const base_url;
    449     const char* const base_suggest_url;
    450   } data[] = {
    451     { "http://google.com/", "http://clients1.google.com/complete/", },
    452     { "http://www.google.com/", "http://clients1.google.com/complete/", },
    453     { "http://www.google.co.uk/", "http://clients1.google.co.uk/complete/", },
    454     { "http://www.google.com.by/",
    455       "http://clients1.google.com.by/complete/", },
    456     { "http://google.com/intl/xx/", "http://clients1.google.com/complete/", },
    457   };
    458 
    459   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i)
    460     CheckSuggestBaseURL(data[i].base_url, data[i].base_suggest_url);
    461 }
    462 
    463 TEST_F(TemplateURLTest, Keyword) {
    464   TemplateURL t_url;
    465   t_url.SetURL("http://www.google.com/search", 0, 0);
    466   EXPECT_FALSE(t_url.autogenerate_keyword());
    467   t_url.set_keyword(ASCIIToUTF16("foo"));
    468   EXPECT_EQ(ASCIIToUTF16("foo"), t_url.keyword());
    469   t_url.set_autogenerate_keyword(true);
    470   EXPECT_TRUE(t_url.autogenerate_keyword());
    471   EXPECT_EQ(ASCIIToUTF16("google.com"), t_url.keyword());
    472   t_url.set_keyword(ASCIIToUTF16("foo"));
    473   EXPECT_FALSE(t_url.autogenerate_keyword());
    474   EXPECT_EQ(ASCIIToUTF16("foo"), t_url.keyword());
    475 }
    476 
    477 TEST_F(TemplateURLTest, ParseParameterKnown) {
    478   std::string parsed_url("{searchTerms}");
    479   TemplateURLRef url_ref(parsed_url, 0, 0);
    480   TemplateURLRef::Replacements replacements;
    481   EXPECT_TRUE(url_ref.ParseParameter(0, 12, &parsed_url, &replacements));
    482   EXPECT_EQ(std::string(), parsed_url);
    483   ASSERT_EQ(1U, replacements.size());
    484   EXPECT_EQ(static_cast<size_t>(0), replacements[0].index);
    485   EXPECT_EQ(TemplateURLRef::SEARCH_TERMS, replacements[0].type);
    486 }
    487 
    488 TEST_F(TemplateURLTest, ParseParameterUnknown) {
    489   std::string parsed_url("{}");
    490   TemplateURLRef url_ref(parsed_url, 0, 0);
    491   TemplateURLRef::Replacements replacements;
    492   EXPECT_FALSE(url_ref.ParseParameter(0, 1, &parsed_url, &replacements));
    493   EXPECT_EQ("{}", parsed_url);
    494   EXPECT_TRUE(replacements.empty());
    495 }
    496 
    497 TEST_F(TemplateURLTest, ParseURLEmpty) {
    498   TemplateURLRef url_ref("", 0, 0);
    499   TemplateURLRef::Replacements replacements;
    500   bool valid = false;
    501   EXPECT_EQ(std::string(), url_ref.ParseURL("", &replacements, &valid));
    502   EXPECT_TRUE(replacements.empty());
    503   EXPECT_TRUE(valid);
    504 }
    505 
    506 TEST_F(TemplateURLTest, ParseURLNoTemplateEnd) {
    507   TemplateURLRef url_ref("{", 0, 0);
    508   TemplateURLRef::Replacements replacements;
    509   bool valid = false;
    510   EXPECT_EQ(std::string(), url_ref.ParseURL("{", &replacements, &valid));
    511   EXPECT_TRUE(replacements.empty());
    512   EXPECT_FALSE(valid);
    513 }
    514 
    515 TEST_F(TemplateURLTest, ParseURLNoKnownParameters) {
    516   TemplateURLRef url_ref("{}", 0, 0);
    517   TemplateURLRef::Replacements replacements;
    518   bool valid = false;
    519   EXPECT_EQ("{}", url_ref.ParseURL("{}", &replacements, &valid));
    520   EXPECT_TRUE(replacements.empty());
    521   EXPECT_TRUE(valid);
    522 }
    523 
    524 TEST_F(TemplateURLTest, ParseURLTwoParameters) {
    525   TemplateURLRef url_ref("{}{{%s}}", 0, 0);
    526   TemplateURLRef::Replacements replacements;
    527   bool valid = false;
    528   EXPECT_EQ("{}{}",
    529             url_ref.ParseURL("{}{{searchTerms}}", &replacements, &valid));
    530   ASSERT_EQ(1U, replacements.size());
    531   EXPECT_EQ(static_cast<size_t>(3), replacements[0].index);
    532   EXPECT_EQ(TemplateURLRef::SEARCH_TERMS, replacements[0].type);
    533   EXPECT_TRUE(valid);
    534 }
    535 
    536 TEST_F(TemplateURLTest, ParseURLNestedParameter) {
    537   TemplateURLRef url_ref("{%s", 0, 0);
    538   TemplateURLRef::Replacements replacements;
    539   bool valid = false;
    540   EXPECT_EQ("{", url_ref.ParseURL("{{searchTerms}", &replacements, &valid));
    541   ASSERT_EQ(1U, replacements.size());
    542   EXPECT_EQ(static_cast<size_t>(1), replacements[0].index);
    543   EXPECT_EQ(TemplateURLRef::SEARCH_TERMS, replacements[0].type);
    544   EXPECT_TRUE(valid);
    545 }
    546