// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "base/message_loop.h" #include "chrome/browser/autocomplete/keyword_provider.h" #include "chrome/browser/template_url.h" #include "chrome/browser/template_url_model.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" class KeywordProviderTest : public testing::Test { protected: struct test_data { const std::wstring input; const int num_results; const std::wstring output[3]; }; KeywordProviderTest() : kw_provider_(NULL) { } virtual ~KeywordProviderTest() { } virtual void SetUp(); virtual void TearDown(); void RunTest(test_data* keyword_cases, int num_cases, std::wstring AutocompleteMatch::* member); protected: scoped_refptr kw_provider_; scoped_ptr model_; }; void KeywordProviderTest::SetUp() { static const TemplateURLModel::Initializer kTestKeywordData[] = { { L"aa", L"aa.com?foo=%s", L"aa" }, { L"aaaa", L"http://aaaa/?aaaa=1&b=%s&c", L"aaaa" }, { L"aaaaa", L"%s", L"aaaaa" }, { L"ab", L"bogus URL %s", L"ab" }, { L"weasel", L"weasel%sweasel", L"weasel" }, { L"www", L" +%2B?=%sfoo ", L"www" }, { L"z", L"%s=z", L"z" }, }; model_.reset(new TemplateURLModel(kTestKeywordData, arraysize(kTestKeywordData))); kw_provider_ = new KeywordProvider(NULL, model_.get()); } void KeywordProviderTest::TearDown() { model_.reset(); kw_provider_ = NULL; } void KeywordProviderTest::RunTest( test_data* keyword_cases, int num_cases, std::wstring AutocompleteMatch::* member) { ACMatches matches; for (int i = 0; i < num_cases; ++i) { AutocompleteInput input(keyword_cases[i].input, std::wstring(), true); kw_provider_->Start(input, false, false); EXPECT_TRUE(kw_provider_->done()); matches = kw_provider_->matches(); EXPECT_EQ(keyword_cases[i].num_results, matches.size()) << L"Input was: " + keyword_cases[i].input; if (matches.size() == keyword_cases[i].num_results) { for (int j = 0; j < keyword_cases[i].num_results; ++j) { EXPECT_EQ(keyword_cases[i].output[j], matches[j].*member); } } } } TEST_F(KeywordProviderTest, Edit) { test_data edit_cases[] = { // Searching for a nonexistent prefix should give nothing. {L"Not Found", 0, {}}, {L"aaaaaNot Found", 0, {}}, // Check that tokenization only collapses whitespace between first tokens, // no-query-input cases have a space appended, and action is not escaped. {L"z foo", 1, {L"z foo"}}, {L"z", 1, {L"z "}}, {L"z \t", 1, {L"z "}}, {L"z a b c++", 1, {L"z a b c++"}}, // Matches should be limited to three, and sorted in quality order, not // alphabetical. {L"aaa", 2, {L"aaaa ", L"aaaaa "}}, {L"a 1 2 3", 3, {L"aa 1 2 3", L"ab 1 2 3", L"aaaa 1 2 3"}}, {L"www.a", 3, {L"aa ", L"ab ", L"aaaa "}}, // Exact matches should prevent returning inexact matches. {L"aaaa foo", 1, {L"aaaa foo"}}, {L"www.aaaa foo", 1, {L"aaaa foo"}}, // Clean up keyword input properly. {L"www", 1, {L"www "}}, {L"www.", 0, {}}, {L"www.w w", 2, {L"www w", L"weasel w"}}, {L"http://www", 1, {L"www "}}, {L"http://www.", 0, {}}, {L"ftp: blah", 0, {}}, {L"mailto:z", 1, {L"z "}}, }; RunTest(edit_cases, arraysize(edit_cases), &AutocompleteMatch::fill_into_edit); } TEST_F(KeywordProviderTest, URL) { test_data url_cases[] = { // No query input -> empty destination URL. {L"z", 1, {L""}}, {L"z \t", 1, {L""}}, // Check that tokenization only collapses whitespace between first tokens // and query input, but not rest of URL, is escaped. {L"z a b c++", 1, {L"a+++b+++c%2B%2B=z"}}, {L"www.www www", 1, {L" +%2B?=wwwfoo "}}, // Substitution should work with various locations of the "%s". {L"aaa 1a2b", 2, {L"http://aaaa/?aaaa=1&b=1a2b&c", L"1a2b"}}, {L"a 1 2 3", 3, {L"aa.com?foo=1+2+3", L"bogus URL 1+2+3", L"http://aaaa/?aaaa=1&b=1+2+3&c"}}, {L"www.w w", 2, {L" +%2B?=wfoo ", L"weaselwweasel"}}, }; RunTest(url_cases, arraysize(url_cases), &AutocompleteMatch::destination_url); } TEST_F(KeywordProviderTest, Contents) { test_data contents_cases[] = { // No query input -> substitute "" into contents. {L"z", 1, {L"Search z for "}}, {L"z \t", 1, {L"Search z for "}}, // Check that tokenization only collapses whitespace between first tokens // and contents are not escaped or unescaped. {L"z a b c++", 1, {L"Search z for a b c++"}}, {L"www.www www", 1, {L"Search www for www"}}, // Substitution should work with various locations of the "%s". {L"aaa", 2, {L"Search aaaa for ", L"Search aaaaa for "}}, {L"a 1 2 3", 3, {L"Search aa for 1 2 3", L"Search ab for 1 2 3", L"Search aaaa for 1 2 3"}}, {L"www.w w", 2, {L"Search www for w", L"Search weasel for w"}}, }; RunTest(contents_cases, arraysize(contents_cases), &AutocompleteMatch::contents); } TEST_F(KeywordProviderTest, Description) { test_data description_cases[] = { // Whole keyword should be returned for both exact and inexact matches. {L"z foo", 1, {L"(Keyword: z)"}}, {L"a foo", 3, {L"(Keyword: aa)", L"(Keyword: ab)", L"(Keyword: aaaa)"}}, {L"ftp://www.www w", 1, {L"(Keyword: www)"}}, // Keyword should be returned regardless of query input. {L"z", 1, {L"(Keyword: z)"}}, {L"z \t", 1, {L"(Keyword: z)"}}, {L"z a b c++", 1, {L"(Keyword: z)"}}, }; RunTest(description_cases, arraysize(description_cases), &AutocompleteMatch::description); } TEST_F(KeywordProviderTest, AddKeyword) { TemplateURL* template_url = new TemplateURL(); std::wstring keyword(L"foo"); std::wstring url(L"http://www.google.com/foo?q={searchTerms}"); template_url->SetURL(url, 0, 0); template_url->set_keyword(keyword); template_url->set_short_name(L"Test"); model_->Add(template_url); ASSERT_TRUE(template_url == model_->GetTemplateURLForKeyword(keyword)); } TEST_F(KeywordProviderTest, RemoveKeyword) { std::wstring url(L"http://aaaa/?aaaa=1&b={searchTerms}&c"); model_->Remove(model_->GetTemplateURLForKeyword(L"aaaa")); ASSERT_TRUE(model_->GetTemplateURLForKeyword(L"aaaa") == NULL); }