blob: 9b4418cb717c79f527d5b32fbed00cb8208e7daf [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/keyword_provider.h"
#include <stddef.h>
#include <map>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_scheme_classifier.h"
#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/search_engines/search_engines_switches.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/variations_associated_data.h"
#include "net/url_request/url_request.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
namespace {
class TestingSchemeClassifier : public AutocompleteSchemeClassifier {
public:
metrics::OmniboxInputType GetInputTypeForScheme(
const std::string& scheme) const override {
if (net::URLRequest::IsHandledProtocol(scheme))
return metrics::OmniboxInputType::URL;
return metrics::OmniboxInputType::INVALID;
}
};
} // namespace
class KeywordProviderTest : public testing::Test {
protected:
template<class ResultType>
struct MatchType {
const ResultType member;
bool allowed_to_be_default_match;
};
template<class ResultType>
struct TestData {
const base::string16 input;
const size_t num_results;
const MatchType<ResultType> output[3];
};
KeywordProviderTest() : kw_provider_(nullptr) {
// Destroy the existing FieldTrialList before creating a new one to avoid
// a DCHECK.
field_trial_list_.reset();
field_trial_list_.reset(new base::FieldTrialList(
std::make_unique<variations::SHA1EntropyProvider>("foo")));
variations::testing::ClearAllVariationParams();
}
~KeywordProviderTest() override {}
// Should be called at least once during a test case. This is a separate
// function from SetUp() because the client may want to set parameters
// (e.g., field trials) before initializing TemplateURLService and the
// related internal variables here.
void SetUpClientAndKeywordProvider();
void TearDown() override;
template<class ResultType>
void RunTest(TestData<ResultType>* keyword_cases,
int num_cases,
ResultType AutocompleteMatch::* member);
protected:
static const TemplateURLService::Initializer kTestData[];
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<base::FieldTrialList> field_trial_list_;
scoped_refptr<KeywordProvider> kw_provider_;
std::unique_ptr<MockAutocompleteProviderClient> client_;
};
// static
const TemplateURLService::Initializer KeywordProviderTest::kTestData[] = {
{"aa", "aa.com?foo={searchTerms}", "aa"},
{"aaaa", "http://aaaa/?aaaa=1&b={searchTerms}&c", "aaaa"},
{"aaaaa", "{searchTerms}", "aaaaa"},
{"ab", "bogus URL {searchTerms}", "ab"},
{"weasel", "weasel{searchTerms}weasel", "weasel"},
{"www", " +%2B?={searchTerms}foo ", "www"},
{"nonsub", "http://nonsubstituting-keyword.com/", "nonsub"},
{"z", "{searchTerms}=z", "z"},
{"host.site.com", "http://host.site.com/?q={searchTerms}", "host.site.com"},
{"ignoremelong.domain.com",
"http://ignoremelong.domain.com/?q={searchTerms}",
"ignoremelong.domain.com"},
{"ignoreme.domain2.com", "http://ignoreme.domain2.com/?q={searchTerms}",
"ignoreme.domain2.com"},
{"fooshort.com", "http://fooshort.com/?q={searchTerms}", "fooshort.com"},
{"foolong.co.uk", "http://foolong.co.uk/?q={searchTerms}", "foolong.co.uk"},
{"cleantestv1.com", "http://cleantestv1.com?q={searchTerms}", "clean v1"},
{"cleantestv2.com", "http://cleantestv2.com?q={searchTerms}", "clean v2"},
{"www.cleantestv2.com", "http://www.cleantestv2.com?q={searchTerms}",
"www clean v2"},
{"www.cleantestv3.com", "http://www.cleantestv3.com?q={searchTerms}",
"www clean v3"},
{"http://cleantestv4.com", "http://cleantestv4.com?q={searchTerms}",
"http clean v4"},
{"cleantestv5.com", "http://cleantestv5.com?q={searchTerms}", "clean v5"},
{"http://cleantestv5.com", "http://cleantestv5.com?q={searchTerms}",
"http clean v5"},
{"cleantestv6:", "http://cleantestv6.com?q={searchTerms}", "clean v6"},
{"cleantestv7/", "http://cleantestv7slash.com?q={searchTerms}",
"clean v7 slash"},
{"cleantestv8/", "http://cleantestv8.com?q={searchTerms}", "clean v8"},
{"cleantestv8", "http://cleantestv8slash.com?q={searchTerms}",
"clean v8 slash"},
};
void KeywordProviderTest::SetUpClientAndKeywordProvider() {
client_.reset(new MockAutocompleteProviderClient());
client_->set_template_url_service(
std::make_unique<TemplateURLService>(kTestData, arraysize(kTestData)));
kw_provider_ = new KeywordProvider(client_.get(), nullptr);
}
void KeywordProviderTest::TearDown() {
client_.reset();
kw_provider_ = nullptr;
}
template<class ResultType>
void KeywordProviderTest::RunTest(TestData<ResultType>* keyword_cases,
int num_cases,
ResultType AutocompleteMatch::* member) {
ACMatches matches;
for (int i = 0; i < num_cases; ++i) {
SCOPED_TRACE(keyword_cases[i].input);
AutocompleteInput input(keyword_cases[i].input,
metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
kw_provider_->Start(input, false);
EXPECT_TRUE(kw_provider_->done());
matches = kw_provider_->matches();
ASSERT_EQ(keyword_cases[i].num_results, matches.size());
for (size_t j = 0; j < matches.size(); ++j) {
EXPECT_EQ(keyword_cases[i].output[j].member, matches[j].*member);
EXPECT_EQ(keyword_cases[i].output[j].allowed_to_be_default_match,
matches[j].allowed_to_be_default_match);
}
}
}
TEST_F(KeywordProviderTest, Edit) {
const MatchType<base::string16> kEmptyMatch = { base::string16(), false };
TestData<base::string16> edit_cases[] = {
// Searching for a nonexistent prefix should give nothing.
{ASCIIToUTF16("Not Found"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("aaaaaNot Found"),
0,
{kEmptyMatch, kEmptyMatch, kEmptyMatch}},
// Check that tokenization only collapses whitespace between first tokens,
// no-query-input cases have a space appended, and action is not escaped.
{ASCIIToUTF16("z"),
1,
{{ASCIIToUTF16("z "), true}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("z \t"),
1,
{{ASCIIToUTF16("z "), true}, kEmptyMatch, kEmptyMatch}},
// Check that exact, substituting keywords with a verbatim search term
// don't generate a result. (These are handled by SearchProvider.)
{ASCIIToUTF16("z foo"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("z a b c++"),
0,
{kEmptyMatch, kEmptyMatch, kEmptyMatch}},
// Matches should be limited to three, and sorted in quality order, not
// alphabetical.
{ASCIIToUTF16("aaa"),
2,
{{ASCIIToUTF16("aaaa "), false},
{ASCIIToUTF16("aaaaa "), false},
kEmptyMatch}},
{ASCIIToUTF16("a 1 2 3"),
3,
{{ASCIIToUTF16("aa 1 2 3"), false},
{ASCIIToUTF16("ab 1 2 3"), false},
{ASCIIToUTF16("aaaa 1 2 3"), false}}},
{ASCIIToUTF16("www.a"),
3,
{{ASCIIToUTF16("aa "), false},
{ASCIIToUTF16("ab "), false},
{ASCIIToUTF16("aaaa "), false}}},
{ASCIIToUTF16("foo hello"),
2,
{{ASCIIToUTF16("fooshort.com hello"), false},
{ASCIIToUTF16("foolong.co.uk hello"), false},
kEmptyMatch}},
// Exact matches should prevent returning inexact matches. Also, the
// verbatim query for this keyword match should not be returned. (It's
// returned by SearchProvider.)
{ASCIIToUTF16("aaaa foo"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("www.aaaa foo"),
0,
{kEmptyMatch, kEmptyMatch, kEmptyMatch}},
// Matches should be retrieved by typing the prefix of the keyword, not
// the
// domain name.
{ASCIIToUTF16("host foo"),
1,
{{ASCIIToUTF16("host.site.com foo"), false}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("host.site foo"),
1,
{{ASCIIToUTF16("host.site.com foo"), false}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("site foo"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
// Clean up keyword input properly. "http" and "https" are the only
// allowed schemes.
{ASCIIToUTF16("www"),
1,
{{ASCIIToUTF16("www "), true}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("www."), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
// In this particular example, stripping the "www." from "www.FOO" means
// we can allow matching against keywords that explicitly start with
// "FOO", even if "FOO" happens to be "www". It's a little odd yet it
// seems reasonable.
{ASCIIToUTF16("www.w w"),
3,
{{ASCIIToUTF16("www w"), false},
{ASCIIToUTF16("weasel w"), false},
{ASCIIToUTF16("www.cleantestv2.com w"), false}}},
{ASCIIToUTF16("http://www"),
1,
{{ASCIIToUTF16("www "), true}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("http://www."), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("ftp: blah"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("mailto:z"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("ftp://z"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("https://z"),
1,
{{ASCIIToUTF16("z "), true}, kEmptyMatch, kEmptyMatch}},
// Non-substituting keywords, whether typed fully or not
// should not add a space.
{ASCIIToUTF16("nonsu"),
1,
{{ASCIIToUTF16("nonsub"), false}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("nonsub"),
1,
{{ASCIIToUTF16("nonsub"), true}, kEmptyMatch, kEmptyMatch}},
};
SetUpClientAndKeywordProvider();
RunTest<base::string16>(edit_cases, arraysize(edit_cases),
&AutocompleteMatch::fill_into_edit);
}
TEST_F(KeywordProviderTest, DomainMatches) {
const MatchType<base::string16> kEmptyMatch = { base::string16(), false };
TestData<base::string16> edit_cases[] = {
// Searching for a nonexistent prefix should give nothing.
{ ASCIIToUTF16("Not Found"), 0,
{ kEmptyMatch, kEmptyMatch, kEmptyMatch } },
{ ASCIIToUTF16("aaaaaNot Found"), 0,
{ kEmptyMatch, kEmptyMatch, kEmptyMatch } },
// Matches should be limited to three and sorted in quality order.
// This order depends on whether we're using the pre-domain-name text
// for matching--when matching the domain, we sort by the length of the
// domain, not the length of the whole keyword.
{ ASCIIToUTF16("ignore foo"), 2,
{ { ASCIIToUTF16("ignoreme.domain2.com foo"), false },
{ ASCIIToUTF16("ignoremelong.domain.com foo"), false },
kEmptyMatch } },
{ ASCIIToUTF16("dom foo"), 2,
{ { ASCIIToUTF16("ignoremelong.domain.com foo"), false },
{ ASCIIToUTF16("ignoreme.domain2.com foo"), false },
kEmptyMatch } },
// Matches should be retrieved by typing the domain name, not only
// a prefix to the keyword.
{ ASCIIToUTF16("host foo"), 1,
{ { ASCIIToUTF16("host.site.com foo"), false },
kEmptyMatch, kEmptyMatch } },
{ ASCIIToUTF16("host.site foo"), 1,
{ { ASCIIToUTF16("host.site.com foo"), false },
kEmptyMatch, kEmptyMatch } },
{ ASCIIToUTF16("site foo"), 1,
{ { ASCIIToUTF16("host.site.com foo"), false },
kEmptyMatch, kEmptyMatch } },
};
// Add a rule enabling matching in the domain name of keywords (i.e.,
// non-prefix matching).
{
std::map<std::string, std::string> params;
params[OmniboxFieldTrial::kKeywordRequiresPrefixMatchRule] = "false";
ASSERT_TRUE(variations::AssociateVariationParams(
OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
}
base::FieldTrialList::CreateFieldTrial(
OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
SetUpClientAndKeywordProvider();
RunTest<base::string16>(edit_cases, arraysize(edit_cases),
&AutocompleteMatch::fill_into_edit);
}
TEST_F(KeywordProviderTest, IgnoreRegistryForScoring) {
const MatchType<base::string16> kEmptyMatch = { base::string16(), false };
TestData<base::string16> edit_cases[] = {
// Matches should be limited to three and sorted in quality order.
// When ignoring the registry length, this order of suggestions should
// result (sorted by keyword length sans registry). The "Edit" test case
// has this exact test for when not ignoring the registry to check that
// the other order (shorter full keyword) results there.
{ ASCIIToUTF16("foo hello"), 2,
{ { ASCIIToUTF16("foolong.co.uk hello"), false },
{ ASCIIToUTF16("fooshort.com hello"), false },
kEmptyMatch } },
// Keywords that don't have full hostnames should keep the same order
// as normal.
{ ASCIIToUTF16("aaa"), 2,
{ { ASCIIToUTF16("aaaa "), false },
{ ASCIIToUTF16("aaaaa "), false },
kEmptyMatch } },
{ ASCIIToUTF16("a 1 2 3"), 3,
{ { ASCIIToUTF16("aa 1 2 3"), false },
{ ASCIIToUTF16("ab 1 2 3"), false },
{ ASCIIToUTF16("aaaa 1 2 3"), false } } },
{ ASCIIToUTF16("www.a"), 3,
{ { ASCIIToUTF16("aa "), false },
{ ASCIIToUTF16("ab "), false },
{ ASCIIToUTF16("aaaa "), false } } },
};
// Add a rule to make matching in the registry portion of a keyword
// unimportant.
{
std::map<std::string, std::string> params;
params[OmniboxFieldTrial::kKeywordRequiresRegistryRule] = "false";
ASSERT_TRUE(variations::AssociateVariationParams(
OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
}
base::FieldTrialList::CreateFieldTrial(
OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
SetUpClientAndKeywordProvider();
RunTest<base::string16>(edit_cases, arraysize(edit_cases),
&AutocompleteMatch::fill_into_edit);
}
TEST_F(KeywordProviderTest, URL) {
const MatchType<GURL> kEmptyMatch = { GURL(), false };
TestData<GURL> url_cases[] = {
// No query input -> empty destination URL.
{ASCIIToUTF16("z"), 1, {{GURL(), true}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("z \t"), 1, {{GURL(), true}, kEmptyMatch, kEmptyMatch}},
// Check that tokenization only collapses whitespace between first tokens
// and query input, but not rest of URL, is escaped.
{ASCIIToUTF16("w bar +baz"),
3,
{{GURL(" +%2B?=bar+%2Bbazfoo "), false},
{GURL("bar+%2Bbaz=z"), false},
{GURL("http://www.cleantestv2.com/?q=bar+%2Bbaz"), false}}},
// Substitution should work with various locations of the "%s".
{ASCIIToUTF16("aaa 1a2b"),
2,
{{GURL("http://aaaa/?aaaa=1&b=1a2b&c"), false},
{GURL("1a2b"), false},
kEmptyMatch}},
{ASCIIToUTF16("a 1 2 3"),
3,
{{GURL("aa.com?foo=1+2+3"), false},
{GURL("bogus URL 1+2+3"), false},
{GURL("http://aaaa/?aaaa=1&b=1+2+3&c"), false}}},
{ASCIIToUTF16("www.w w"),
3,
{{GURL(" +%2B?=wfoo "), false},
{GURL("weaselwweasel"), false},
{GURL("http://www.cleantestv2.com/?q=w"), false}}},
};
SetUpClientAndKeywordProvider();
RunTest<GURL>(url_cases, arraysize(url_cases),
&AutocompleteMatch::destination_url);
}
TEST_F(KeywordProviderTest, Contents) {
const MatchType<base::string16> kEmptyMatch = { base::string16(), false };
TestData<base::string16> contents_cases[] = {
// No query input -> substitute "<Type search term>" into contents.
{ASCIIToUTF16("z"),
1,
{{ASCIIToUTF16("<Type search term>"), true}, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("z \t"),
1,
{{ASCIIToUTF16("<Type search term>"), true}, kEmptyMatch, kEmptyMatch}},
// Exact keyword matches with remaining text should return nothing.
{ASCIIToUTF16("www.www www"), 0, {kEmptyMatch, kEmptyMatch, kEmptyMatch}},
{ASCIIToUTF16("z a b c++"),
0,
{kEmptyMatch, kEmptyMatch, kEmptyMatch}},
// Exact keyword matches with remaining text when the keyword is an
// extension keyword should return something. This is tested in
// chrome/browser/extensions/api/omnibox/omnibox_apitest.cc's
// in OmniboxApiTest's Basic test.
// There are two keywords that start with "aaa". Suggestions will be
// disambiguated by the description. We do not test the description value
// here because KeywordProvider doesn't set descriptions; these are
// populated later by AutocompleteController.
{ASCIIToUTF16("aaa"),
2,
{{ASCIIToUTF16("<Type search term>"), false},
{ASCIIToUTF16("<Type search term>"), false},
kEmptyMatch}},
// When there is a search string, simply display it.
{ASCIIToUTF16("www.w w"),
3,
{{ASCIIToUTF16("w"), false},
{ASCIIToUTF16("w"), false},
{ASCIIToUTF16("w"), false}}},
// Also, check that tokenization only collapses whitespace between first
// tokens and contents are not escaped or unescaped.
{ASCIIToUTF16("a 1 2+ 3"),
3,
{{ASCIIToUTF16("1 2+ 3"), false},
{ASCIIToUTF16("1 2+ 3"), false},
{ASCIIToUTF16("1 2+ 3"), false}}},
};
SetUpClientAndKeywordProvider();
RunTest<base::string16>(contents_cases, arraysize(contents_cases),
&AutocompleteMatch::contents);
}
TEST_F(KeywordProviderTest, AddKeyword) {
SetUpClientAndKeywordProvider();
TemplateURLData data;
data.SetShortName(ASCIIToUTF16("Test"));
base::string16 keyword(ASCIIToUTF16("foo"));
data.SetKeyword(keyword);
data.SetURL("http://www.google.com/foo?q={searchTerms}");
TemplateURL* template_url = client_->GetTemplateURLService()->Add(
std::make_unique<TemplateURL>(data));
ASSERT_TRUE(
template_url ==
client_->GetTemplateURLService()->GetTemplateURLForKeyword(keyword));
}
TEST_F(KeywordProviderTest, RemoveKeyword) {
SetUpClientAndKeywordProvider();
TemplateURLService* template_url_service = client_->GetTemplateURLService();
base::string16 url(ASCIIToUTF16("http://aaaa/?aaaa=1&b={searchTerms}&c"));
template_url_service->Remove(
template_url_service->GetTemplateURLForKeyword(ASCIIToUTF16("aaaa")));
ASSERT_TRUE(template_url_service->GetTemplateURLForKeyword(
ASCIIToUTF16("aaaa")) == nullptr);
}
TEST_F(KeywordProviderTest, GetKeywordForInput) {
SetUpClientAndKeywordProvider();
EXPECT_EQ(ASCIIToUTF16("aa"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("aa")));
EXPECT_EQ(base::string16(),
kw_provider_->GetKeywordForText(ASCIIToUTF16("aafoo")));
EXPECT_EQ(base::string16(),
kw_provider_->GetKeywordForText(ASCIIToUTF16("aa foo")));
EXPECT_EQ(
ASCIIToUTF16("cleantestv1.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("http://cleantestv1.com")));
EXPECT_EQ(
ASCIIToUTF16("cleantestv1.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("www.cleantestv1.com")));
EXPECT_EQ(ASCIIToUTF16("cleantestv1.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv1.com/")));
EXPECT_EQ(ASCIIToUTF16("cleantestv1.com"),
kw_provider_->GetKeywordForText(
ASCIIToUTF16("https://www.cleantestv1.com/")));
EXPECT_EQ(ASCIIToUTF16("cleantestv2.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv2.com")));
EXPECT_EQ(
ASCIIToUTF16("www.cleantestv2.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("www.cleantestv2.com")));
EXPECT_EQ(ASCIIToUTF16("cleantestv2.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv2.com/")));
EXPECT_EQ(
ASCIIToUTF16("www.cleantestv3.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("www.cleantestv3.com")));
EXPECT_EQ(base::string16(),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv3.com")));
EXPECT_EQ(
ASCIIToUTF16("http://cleantestv4.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("http://cleantestv4.com")));
EXPECT_EQ(base::string16(),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv4.com")));
EXPECT_EQ(ASCIIToUTF16("cleantestv5.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv5.com")));
EXPECT_EQ(
ASCIIToUTF16("http://cleantestv5.com"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("http://cleantestv5.com")));
EXPECT_EQ(ASCIIToUTF16("cleantestv6:"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv6:")));
EXPECT_EQ(base::string16(),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv6")));
EXPECT_EQ(ASCIIToUTF16("cleantestv7/"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv7/")));
EXPECT_EQ(base::string16(),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv7")));
EXPECT_EQ(ASCIIToUTF16("cleantestv8/"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv8/")));
EXPECT_EQ(ASCIIToUTF16("cleantestv8"),
kw_provider_->GetKeywordForText(ASCIIToUTF16("cleantestv8")));
}
TEST_F(KeywordProviderTest, GetSubstitutingTemplateURLForInput) {
struct {
const std::string text;
const size_t cursor_position;
const bool allow_exact_keyword_match;
const std::string expected_url;
const std::string updated_text;
const size_t updated_cursor_position;
} cases[] = {
{ "foo", base::string16::npos, true, "", "foo", base::string16::npos },
{ "aa foo", base::string16::npos, true, "aa.com?foo={searchTerms}", "foo",
base::string16::npos },
// Cursor adjustment.
{ "aa foo", base::string16::npos, true, "aa.com?foo={searchTerms}", "foo",
base::string16::npos },
{ "aa foo", 4u, true, "aa.com?foo={searchTerms}", "foo", 1u },
// Cursor at the end.
{ "aa foo", 6u, true, "aa.com?foo={searchTerms}", "foo", 3u },
// Cursor before the first character of the remaining text.
{ "aa foo", 3u, true, "aa.com?foo={searchTerms}", "foo", 0u },
// Trailing space.
{ "aa foo ", 7u, true, "aa.com?foo={searchTerms}", "foo ", 4u },
// Trailing space without remaining text, cursor in the middle.
{ "aa ", 3u, true, "aa.com?foo={searchTerms}", "", base::string16::npos },
// Trailing space without remaining text, cursor at the end.
{ "aa ", 4u, true, "aa.com?foo={searchTerms}", "", base::string16::npos },
// Extra space after keyword, cursor at the end.
{ "aa foo ", 8u, true, "aa.com?foo={searchTerms}", "foo ", 4u },
// Extra space after keyword, cursor in the middle.
{ "aa foo ", 3u, true, "aa.com?foo={searchTerms}", "foo ", 0 },
// Extra space after keyword, no trailing space, cursor at the end.
{ "aa foo", 7u, true, "aa.com?foo={searchTerms}", "foo", 3u },
// Extra space after keyword, no trailing space, cursor in the middle.
{ "aa foo", 5u, true, "aa.com?foo={searchTerms}", "foo", 1u },
// Disallow exact keyword match.
{ "aa foo", base::string16::npos, false, "", "aa foo",
base::string16::npos },
};
SetUpClientAndKeywordProvider();
for (size_t i = 0; i < arraysize(cases); i++) {
AutocompleteInput input(
ASCIIToUTF16(cases[i].text), cases[i].cursor_position,
metrics::OmniboxEventProto::OTHER, TestingSchemeClassifier());
input.set_allow_exact_keyword_match(cases[i].allow_exact_keyword_match);
const TemplateURL* url =
KeywordProvider::GetSubstitutingTemplateURLForInput(
client_->GetTemplateURLService(), &input);
if (cases[i].expected_url.empty())
EXPECT_FALSE(url);
else
EXPECT_EQ(cases[i].expected_url, url->url());
EXPECT_EQ(ASCIIToUTF16(cases[i].updated_text), input.text());
EXPECT_EQ(cases[i].updated_cursor_position, input.cursor_position());
}
}
// If extra query params are specified on the command line, they should be
// reflected (only) in the default search provider's destination URL.
TEST_F(KeywordProviderTest, ExtraQueryParams) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kExtraSearchQueryParams, "a=b");
TestData<GURL> url_cases[] = {
{ ASCIIToUTF16("a 1 2 3"), 3,
{ { GURL("aa.com?a=b&foo=1+2+3"), false },
{ GURL("bogus URL 1+2+3"), false },
{ GURL("http://aaaa/?aaaa=1&b=1+2+3&c"), false } } },
};
SetUpClientAndKeywordProvider();
RunTest<GURL>(url_cases, arraysize(url_cases),
&AutocompleteMatch::destination_url);
}
TEST_F(KeywordProviderTest, DoesNotProvideMatchesOnFocus) {
SetUpClientAndKeywordProvider();
AutocompleteInput input(ASCIIToUTF16("aaa"),
metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
input.set_from_omnibox_focus(true);
kw_provider_->Start(input, false);
ASSERT_TRUE(kw_provider_->matches().empty());
}