blob: ffc8f980aa6c3ec3720a830165b603daf13f8b52 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/search_engines/template_url_prepopulate_data.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/to_vector.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "components/country_codes/country_codes.h"
#include "components/google/core/common/google_switches.h"
#include "components/search_engines/prepopulated_engines.h"
#include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
#include "components/search_engines/search_engine_choice/search_engine_choice_utils.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/search_engines_switches.h"
#include "components/search_engines/search_engines_test_util.h"
#include "components/search_engines/search_terms_data.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_data_util.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/testing_search_terms_data.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::ASCIIToUTF16;
namespace {
SearchEngineType GetEngineType(const std::string& url) {
TemplateURLData data;
data.SetURL(url);
return TemplateURL(data).GetEngineType(SearchTermsData());
}
std::string GetHostFromTemplateURLData(const TemplateURLData& data) {
return TemplateURL(data).url_ref().GetHost(SearchTermsData());
}
const int kAllCountryIds[] = {'A' << 8 | 'D', 'A' << 8 | 'E', 'A' << 8 | 'F',
'A' << 8 | 'G', 'A' << 8 | 'I', 'A' << 8 | 'L',
'A' << 8 | 'M', 'A' << 8 | 'N', 'A' << 8 | 'O',
'A' << 8 | 'Q', 'A' << 8 | 'R', 'A' << 8 | 'S',
'A' << 8 | 'T', 'A' << 8 | 'U', 'A' << 8 | 'W',
'A' << 8 | 'X', 'A' << 8 | 'Z', 'B' << 8 | 'A',
'B' << 8 | 'B', 'B' << 8 | 'D', 'B' << 8 | 'E',
'B' << 8 | 'F', 'B' << 8 | 'G', 'B' << 8 | 'H',
'B' << 8 | 'I', 'B' << 8 | 'J', 'B' << 8 | 'M',
'B' << 8 | 'N', 'B' << 8 | 'O', 'B' << 8 | 'R',
'B' << 8 | 'S', 'B' << 8 | 'T', 'B' << 8 | 'V',
'B' << 8 | 'W', 'B' << 8 | 'Y', 'B' << 8 | 'Z',
'C' << 8 | 'A', 'C' << 8 | 'C', 'C' << 8 | 'D',
'C' << 8 | 'F', 'C' << 8 | 'G', 'C' << 8 | 'H',
'C' << 8 | 'I', 'C' << 8 | 'K', 'C' << 8 | 'L',
'C' << 8 | 'M', 'C' << 8 | 'N', 'C' << 8 | 'O',
'C' << 8 | 'Q', 'C' << 8 | 'R', 'C' << 8 | 'U',
'C' << 8 | 'V', 'C' << 8 | 'X', 'C' << 8 | 'Y',
'C' << 8 | 'Z', 'D' << 8 | 'E', 'D' << 8 | 'J',
'D' << 8 | 'K', 'D' << 8 | 'M', 'D' << 8 | 'O',
'D' << 8 | 'Z', 'E' << 8 | 'C', 'E' << 8 | 'E',
'E' << 8 | 'G', 'E' << 8 | 'R', 'E' << 8 | 'S',
'E' << 8 | 'T', 'F' << 8 | 'I', 'F' << 8 | 'J',
'F' << 8 | 'K', 'F' << 8 | 'M', 'F' << 8 | 'O',
'F' << 8 | 'R', 'G' << 8 | 'A', 'G' << 8 | 'B',
'G' << 8 | 'D', 'G' << 8 | 'E', 'G' << 8 | 'F',
'G' << 8 | 'G', 'G' << 8 | 'H', 'G' << 8 | 'I',
'G' << 8 | 'L', 'G' << 8 | 'M', 'G' << 8 | 'N',
'G' << 8 | 'P', 'G' << 8 | 'Q', 'G' << 8 | 'R',
'G' << 8 | 'S', 'G' << 8 | 'T', 'G' << 8 | 'U',
'G' << 8 | 'W', 'G' << 8 | 'Y', 'H' << 8 | 'K',
'H' << 8 | 'M', 'H' << 8 | 'N', 'H' << 8 | 'R',
'H' << 8 | 'T', 'H' << 8 | 'U', 'I' << 8 | 'D',
'I' << 8 | 'E', 'I' << 8 | 'L', 'I' << 8 | 'M',
'I' << 8 | 'N', 'I' << 8 | 'O', 'I' << 8 | 'P',
'I' << 8 | 'Q', 'I' << 8 | 'R', 'I' << 8 | 'S',
'I' << 8 | 'T', 'J' << 8 | 'E', 'J' << 8 | 'M',
'J' << 8 | 'O', 'J' << 8 | 'P', 'K' << 8 | 'E',
'K' << 8 | 'G', 'K' << 8 | 'H', 'K' << 8 | 'I',
'K' << 8 | 'M', 'K' << 8 | 'N', 'K' << 8 | 'P',
'K' << 8 | 'R', 'K' << 8 | 'W', 'K' << 8 | 'Y',
'K' << 8 | 'Z', 'L' << 8 | 'A', 'L' << 8 | 'B',
'L' << 8 | 'C', 'L' << 8 | 'I', 'L' << 8 | 'K',
'L' << 8 | 'R', 'L' << 8 | 'S', 'L' << 8 | 'T',
'L' << 8 | 'U', 'L' << 8 | 'V', 'L' << 8 | 'Y',
'M' << 8 | 'A', 'M' << 8 | 'C', 'M' << 8 | 'D',
'M' << 8 | 'E', 'M' << 8 | 'G', 'M' << 8 | 'H',
'M' << 8 | 'K', 'M' << 8 | 'L', 'M' << 8 | 'M',
'M' << 8 | 'N', 'M' << 8 | 'O', 'M' << 8 | 'P',
'M' << 8 | 'Q', 'M' << 8 | 'R', 'M' << 8 | 'S',
'M' << 8 | 'T', 'M' << 8 | 'U', 'M' << 8 | 'V',
'M' << 8 | 'W', 'M' << 8 | 'X', 'M' << 8 | 'Y',
'M' << 8 | 'Z', 'N' << 8 | 'A', 'N' << 8 | 'C',
'N' << 8 | 'E', 'N' << 8 | 'F', 'N' << 8 | 'G',
'N' << 8 | 'I', 'N' << 8 | 'L', 'N' << 8 | 'O',
'N' << 8 | 'P', 'N' << 8 | 'R', 'N' << 8 | 'U',
'N' << 8 | 'Z', 'O' << 8 | 'M', 'P' << 8 | 'A',
'P' << 8 | 'E', 'P' << 8 | 'F', 'P' << 8 | 'G',
'P' << 8 | 'H', 'P' << 8 | 'K', 'P' << 8 | 'L',
'P' << 8 | 'M', 'P' << 8 | 'N', 'P' << 8 | 'R',
'P' << 8 | 'S', 'P' << 8 | 'T', 'P' << 8 | 'W',
'P' << 8 | 'Y', 'Q' << 8 | 'A', 'R' << 8 | 'E',
'R' << 8 | 'O', 'R' << 8 | 'S', 'R' << 8 | 'U',
'R' << 8 | 'W', 'S' << 8 | 'A', 'S' << 8 | 'B',
'S' << 8 | 'C', 'S' << 8 | 'D', 'S' << 8 | 'E',
'S' << 8 | 'G', 'S' << 8 | 'H', 'S' << 8 | 'I',
'S' << 8 | 'J', 'S' << 8 | 'K', 'S' << 8 | 'L',
'S' << 8 | 'M', 'S' << 8 | 'N', 'S' << 8 | 'O',
'S' << 8 | 'R', 'S' << 8 | 'T', 'S' << 8 | 'V',
'S' << 8 | 'Y', 'S' << 8 | 'Z', 'T' << 8 | 'C',
'T' << 8 | 'D', 'T' << 8 | 'F', 'T' << 8 | 'G',
'T' << 8 | 'H', 'T' << 8 | 'J', 'T' << 8 | 'K',
'T' << 8 | 'L', 'T' << 8 | 'M', 'T' << 8 | 'N',
'T' << 8 | 'O', 'T' << 8 | 'R', 'T' << 8 | 'T',
'T' << 8 | 'V', 'T' << 8 | 'W', 'T' << 8 | 'Z',
'U' << 8 | 'A', 'U' << 8 | 'G', 'U' << 8 | 'M',
'U' << 8 | 'S', 'U' << 8 | 'Y', 'U' << 8 | 'Z',
'V' << 8 | 'A', 'V' << 8 | 'C', 'V' << 8 | 'E',
'V' << 8 | 'G', 'V' << 8 | 'I', 'V' << 8 | 'N',
'V' << 8 | 'U', 'W' << 8 | 'F', 'W' << 8 | 'S',
'Y' << 8 | 'E', 'Y' << 8 | 'T', 'Z' << 8 | 'A',
'Z' << 8 | 'M', 'Z' << 8 | 'W', -1};
void CheckUrlIsEmptyOrSecure(const std::string url) {
ASSERT_TRUE(url.empty() || url.starts_with("{google:") ||
url.starts_with(url::kHttpsScheme));
}
void CheckTemplateUrlRefIsCryptographic(const TemplateURLRef& url_ref) {
TestingSearchTermsData search_terms_data("https://www.google.com/");
if (!url_ref.IsValid(search_terms_data)) {
ADD_FAILURE() << url_ref.GetURL();
return;
}
// Double parentheses around the string16 constructor to prevent the compiler
// from parsing it as a function declaration.
TemplateURLRef::SearchTermsArgs search_term_args((std::u16string()));
GURL url(url_ref.ReplaceSearchTerms(search_term_args, search_terms_data));
EXPECT_TRUE(url.is_empty() || url.SchemeIsCryptographic()) << url;
}
} // namespace
class TemplateURLPrepopulateDataTest : public testing::Test {
public:
void SetUp() override {
TemplateURLPrepopulateData::RegisterProfilePrefs(prefs_.registry());
TemplateURLService::RegisterProfilePrefs(prefs_.registry());
search_engine_choice_service_ =
std::make_unique<search_engines::SearchEngineChoiceService>(prefs_);
}
search_engines::SearchEngineChoiceService* search_engine_choice_service() {
return search_engine_choice_service_.get();
}
void SetupForChoiceScreenDisplay() {
feature_list_.Reset();
feature_list_.InitAndEnableFeature(switches::kSearchEngineChoiceTrigger);
// Pick any EEA country
const int kFranceCountryId =
country_codes::CountryCharsToCountryID('F', 'R');
OverrideCountryId(kFranceCountryId);
}
void OverrideCountryId(int country_id) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSearchEngineChoiceCountry)) {
base::CommandLine::ForCurrentProcess()->RemoveSwitch(
switches::kSearchEngineChoiceCountry);
}
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kSearchEngineChoiceCountry,
country_codes::CountryIDToCountryString(country_id));
}
protected:
base::test::ScopedFeatureList feature_list_;
sync_preferences::TestingPrefServiceSyncable prefs_;
std::unique_ptr<search_engines::SearchEngineChoiceService>
search_engine_choice_service_;
};
// Verifies the set of prepopulate data doesn't contain entries with duplicate
// ids.
TEST_F(TemplateURLPrepopulateDataTest, UniqueIDs) {
for (int country_id : kAllCountryIds) {
OverrideCountryId(country_id);
std::vector<std::unique_ptr<TemplateURLData>> urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), nullptr);
std::set<int> unique_ids;
for (const std::unique_ptr<TemplateURLData>& url : urls) {
ASSERT_TRUE(unique_ids.find(url->prepopulate_id) == unique_ids.end());
unique_ids.insert(url->prepopulate_id);
}
}
}
// Verifies that the prepopulated search engines configured by country are
// consistent with the set of countries in EeaChoiceCountry. For example, the
// per region limits `kMaxEeaPrepopulatedEngines` and
// `kMaxRowPrepopulatedEngines` should apply as expected.
TEST_F(TemplateURLPrepopulateDataTest, NumberOfEntriesPerCountryConsistency) {
feature_list_.Reset();
feature_list_.InitAndEnableFeature(switches::kSearchEngineChoiceTrigger);
const size_t kMinEea = 8;
const size_t kMinRow = 3;
for (int country_id : kAllCountryIds) {
OverrideCountryId(country_id);
const size_t kNumberOfSearchEngines =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(),
/*default_search_provider_index=*/nullptr)
.size();
if (search_engines::IsEeaChoiceCountry(country_id)) {
EXPECT_GE(kNumberOfSearchEngines, kMinEea)
<< " for country "
<< country_codes::CountryIDToCountryString(country_id);
EXPECT_LE(kNumberOfSearchEngines,
TemplateURLPrepopulateData::kMaxEeaPrepopulatedEngines)
<< " for country "
<< country_codes::CountryIDToCountryString(country_id);
} else {
EXPECT_GE(kNumberOfSearchEngines, kMinRow)
<< " for country "
<< country_codes::CountryIDToCountryString(country_id);
EXPECT_LE(kNumberOfSearchEngines,
TemplateURLPrepopulateData::kMaxRowPrepopulatedEngines)
<< " for country "
<< country_codes::CountryIDToCountryString(country_id);
}
}
}
TEST_F(TemplateURLPrepopulateDataTest, EntriesPerCountryConsistency) {
feature_list_.Reset();
feature_list_.InitAndEnableFeature(switches::kSearchEngineChoiceTrigger);
for (int country_id : kAllCountryIds) {
if (!search_engines::IsEeaChoiceCountry(country_id)) {
// "unhandled" countries can cause some issues when inheriting a config
// from an EEA country. Covering them via
// TemplateURLPrepopulateDataTest.NumberOfEntriesPerCountryConsistency is
// enough, so they we exclude non-EEA countries in the rest of this test
// for simplicity.
continue;
}
OverrideCountryId(country_id);
// Obtained by calling the normal API to fetch engines for the current
// country.
std::vector<std::string> actual_urls =
base::ToVector(TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(),
/*default_search_provider_index=*/nullptr),
[](const auto& t_url) { return t_url->url(); });
// Pulled straight from the country -> engine mapping.
auto expected_urls = base::ToVector(
TemplateURLPrepopulateData::GetPrepopulationSetFromCountryIDForTesting(
country_id),
&TemplateURLPrepopulateData::PrepopulatedEngine::search_url);
EXPECT_THAT(actual_urls, testing::UnorderedElementsAreArray(expected_urls));
}
}
// Verifies that the order of the randomly shuffled search engines stays
// constant per-profile.
TEST_F(TemplateURLPrepopulateDataTest,
SearchEnginesOrderDoesNotChangePerProfile) {
SetupForChoiceScreenDisplay();
// Fetch the list of search engines twice and make sure the order stays the
// same.
std::vector<std::unique_ptr<TemplateURLData>> t_urls_1 =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(),
/*default_search_provider_index=*/nullptr);
std::vector<std::unique_ptr<TemplateURLData>> t_urls_2 =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(),
/*default_search_provider_index=*/nullptr);
ASSERT_EQ(t_urls_1.size(), t_urls_2.size());
for (size_t i = 0; i < t_urls_1.size(); i++) {
// Each prepopulated engine has a unique prepopulate_id, so we simply
// compare those.
ASSERT_EQ(t_urls_1[i]->prepopulate_id, t_urls_2[i]->prepopulate_id);
}
}
// Verifies that the the search engines are re-shuffled on Chrome update.
TEST_F(TemplateURLPrepopulateDataTest,
SearchEnginesOrderChangesOnChromeUpdate) {
SetupForChoiceScreenDisplay();
std::vector<std::unique_ptr<TemplateURLData>> t_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(),
/*default_search_provider_index=*/nullptr);
// Change the saved chrome milestone to something else.
prefs_.SetInteger(prefs::kDefaultSearchProviderChoiceScreenShuffleMilestone,
3);
std::vector<std::unique_ptr<TemplateURLData>> t_urls_after_update =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(),
/*default_search_provider_index=*/nullptr);
ASSERT_EQ(t_urls.size(), t_urls_after_update.size());
bool is_order_same = true;
for (size_t i = 0; i < t_urls.size(); i++) {
// Each prepopulated engine has a unique prepopulate_id, so we simply
// compare those.
is_order_same &=
t_urls[i]->prepopulate_id == t_urls_after_update[i]->prepopulate_id;
if (!is_order_same) {
break;
}
}
ASSERT_FALSE(is_order_same);
}
// Verifies that default search providers from the preferences file
// override the built-in ones.
TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrefs) {
prefs_.SetUserPref(prefs::kSearchProviderOverridesVersion,
std::make_unique<base::Value>(1));
base::Value::List overrides;
// Set only the minimal required settings for a search provider configuration.
base::Value::Dict entry =
base::Value::Dict()
.Set("name", "foo")
.Set("keyword", "fook")
.Set("search_url", "http://foo.com/s?q={searchTerms}")
.Set("favicon_url", "http://foi.com/favicon.ico")
.Set("encoding", "UTF-8")
.Set("id", 1001);
overrides.Append(entry.Clone());
prefs_.SetUserPref(prefs::kSearchProviderOverrides, std::move(overrides));
int version = TemplateURLPrepopulateData::GetDataVersion(&prefs_);
EXPECT_EQ(1, version);
size_t default_index;
std::vector<std::unique_ptr<TemplateURLData>> t_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &default_index);
ASSERT_EQ(1u, t_urls.size());
EXPECT_EQ(u"foo", t_urls[0]->short_name());
EXPECT_EQ(u"fook", t_urls[0]->keyword());
EXPECT_EQ("foo.com", GetHostFromTemplateURLData(*t_urls[0]));
EXPECT_EQ("foi.com", t_urls[0]->favicon_url.host());
EXPECT_EQ(1u, t_urls[0]->input_encodings.size());
EXPECT_EQ(1001, t_urls[0]->prepopulate_id);
EXPECT_TRUE(t_urls[0]->suggestions_url.empty());
EXPECT_EQ(0u, t_urls[0]->alternate_urls.size());
EXPECT_TRUE(t_urls[0]->safe_for_autoreplace);
EXPECT_TRUE(t_urls[0]->date_created.is_null());
EXPECT_TRUE(t_urls[0]->last_modified.is_null());
// Test the optional settings too.
entry.Set("suggest_url", "http://foo.com/suggest?q={searchTerms}");
entry.Set("alternate_urls", base::Value::List().Append(
"http://foo.com/alternate?q={searchTerms}"));
overrides = base::Value::List().Append(entry.Clone());
prefs_.SetUserPref(prefs::kSearchProviderOverrides, std::move(overrides));
t_urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &default_index);
ASSERT_EQ(1u, t_urls.size());
EXPECT_EQ(u"foo", t_urls[0]->short_name());
EXPECT_EQ(u"fook", t_urls[0]->keyword());
EXPECT_EQ("foo.com", GetHostFromTemplateURLData(*t_urls[0]));
EXPECT_EQ("foi.com", t_urls[0]->favicon_url.host());
EXPECT_EQ(1u, t_urls[0]->input_encodings.size());
EXPECT_EQ(1001, t_urls[0]->prepopulate_id);
EXPECT_EQ("http://foo.com/suggest?q={searchTerms}",
t_urls[0]->suggestions_url);
ASSERT_EQ(1u, t_urls[0]->alternate_urls.size());
EXPECT_EQ("http://foo.com/alternate?q={searchTerms}",
t_urls[0]->alternate_urls[0]);
// Test that subsequent providers are loaded even if an intermediate
// provider has an incomplete configuration.
overrides = base::Value::List().Append(entry.Clone());
entry.Set("id", 1002);
entry.Set("name", "bar");
entry.Set("keyword", "bark");
entry.Set("encoding", std::string());
overrides.Append(entry.Clone());
entry.Set("id", 1003);
entry.Set("name", "baz");
entry.Set("keyword", "bazk");
entry.Set("encoding", "UTF-8");
overrides.Append(entry.Clone());
prefs_.SetUserPref(prefs::kSearchProviderOverrides, std::move(overrides));
t_urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &default_index);
EXPECT_EQ(2u, t_urls.size());
}
TEST_F(TemplateURLPrepopulateDataTest, ClearProvidersFromPrefs) {
OverrideCountryId(country_codes::kCountryIDUnknown);
prefs_.SetUserPref(prefs::kSearchProviderOverridesVersion,
std::make_unique<base::Value>(1));
// Set only the minimal required settings for a search provider configuration.
base::Value::Dict entry =
base::Value::Dict()
.Set("name", "foo")
.Set("keyword", "fook")
.Set("search_url", "http://foo.com/s?q={searchTerms}")
.Set("favicon_url", "http://foi.com/favicon.ico")
.Set("encoding", "UTF-8")
.Set("id", 1001);
base::Value::List overrides = base::Value::List().Append(std::move(entry));
prefs_.SetUserPref(prefs::kSearchProviderOverrides, std::move(overrides));
int version = TemplateURLPrepopulateData::GetDataVersion(&prefs_);
EXPECT_EQ(1, version);
// This call removes the above search engine.
TemplateURLPrepopulateData::ClearPrepopulatedEnginesInPrefs(&prefs_);
version = TemplateURLPrepopulateData::GetDataVersion(&prefs_);
EXPECT_EQ(TemplateURLPrepopulateData::kCurrentDataVersion, version);
size_t default_index;
std::vector<std::unique_ptr<TemplateURLData>> t_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &default_index);
ASSERT_FALSE(t_urls.empty());
for (size_t i = 0; i < t_urls.size(); ++i) {
EXPECT_NE(u"foo", t_urls[i]->short_name());
EXPECT_NE(u"fook", t_urls[i]->keyword());
EXPECT_NE("foi.com", t_urls[i]->favicon_url.host());
EXPECT_NE("foo.com", GetHostFromTemplateURLData(*t_urls[i]));
EXPECT_NE(1001, t_urls[i]->prepopulate_id);
}
// Ensures the default URL is Google and has the optional fields filled.
EXPECT_EQ(u"Google", t_urls[default_index]->short_name());
EXPECT_FALSE(t_urls[default_index]->suggestions_url.empty());
EXPECT_FALSE(t_urls[default_index]->image_url.empty());
EXPECT_FALSE(t_urls[default_index]->contextual_search_url.empty());
EXPECT_FALSE(t_urls[default_index]->image_url_post_params.empty());
EXPECT_EQ(SEARCH_ENGINE_GOOGLE,
TemplateURL(*t_urls[default_index]).GetEngineType(
SearchTermsData()));
}
// Verifies that built-in search providers are processed correctly.
TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrepopulated) {
// Use United States.
OverrideCountryId(country_codes::CountryCharsToCountryID('U', 'S'));
size_t default_index;
std::vector<std::unique_ptr<TemplateURLData>> t_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &default_index);
// Ensure all the URLs have the required fields populated.
ASSERT_FALSE(t_urls.empty());
for (size_t i = 0; i < t_urls.size(); ++i) {
ASSERT_FALSE(t_urls[i]->short_name().empty());
ASSERT_FALSE(t_urls[i]->keyword().empty());
ASSERT_FALSE(t_urls[i]->favicon_url.host().empty());
ASSERT_FALSE(GetHostFromTemplateURLData(*t_urls[i]).empty());
ASSERT_FALSE(t_urls[i]->input_encodings.empty());
EXPECT_GT(t_urls[i]->prepopulate_id, 0);
EXPECT_TRUE(t_urls[0]->safe_for_autoreplace);
EXPECT_TRUE(t_urls[0]->date_created.is_null());
EXPECT_TRUE(t_urls[0]->last_modified.is_null());
}
// Ensures the default URL is Google and has the optional fields filled.
EXPECT_EQ(u"Google", t_urls[default_index]->short_name());
EXPECT_FALSE(t_urls[default_index]->suggestions_url.empty());
EXPECT_FALSE(t_urls[default_index]->image_url.empty());
EXPECT_FALSE(t_urls[default_index]->contextual_search_url.empty());
EXPECT_FALSE(t_urls[default_index]->image_url_post_params.empty());
// Expect at least 2 alternate_urls.
// This caught a bug with static initialization of arrays, so leave this in.
EXPECT_GT(t_urls[default_index]->alternate_urls.size(), 1u);
for (size_t i = 0; i < t_urls[default_index]->alternate_urls.size(); ++i)
EXPECT_FALSE(t_urls[default_index]->alternate_urls[i].empty());
EXPECT_EQ(SEARCH_ENGINE_GOOGLE,
TemplateURL(*t_urls[default_index]).GetEngineType(
SearchTermsData()));
}
// Verifies that all built-in search providers available across all countries
// use https urls.
TEST_F(TemplateURLPrepopulateDataTest, PrepopulatedAreHttps) {
for (int country_id : kAllCountryIds) {
OverrideCountryId(country_id);
std::vector<std::unique_ptr<TemplateURLData>> t_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), nullptr);
ASSERT_FALSE(t_urls.empty());
for (const auto& t_url : t_urls) {
CheckUrlIsEmptyOrSecure(t_url->url());
CheckUrlIsEmptyOrSecure(t_url->image_url);
CheckUrlIsEmptyOrSecure(t_url->image_translate_url);
CheckUrlIsEmptyOrSecure(t_url->new_tab_url);
CheckUrlIsEmptyOrSecure(t_url->contextual_search_url);
CheckUrlIsEmptyOrSecure(t_url->suggestions_url);
CheckUrlIsEmptyOrSecure(t_url->favicon_url.scheme());
CheckUrlIsEmptyOrSecure(t_url->logo_url.scheme());
}
}
}
TEST_F(TemplateURLPrepopulateDataTest, GetEngineTypeBasic) {
EXPECT_EQ(SEARCH_ENGINE_OTHER, GetEngineType("http://example.com/"));
EXPECT_EQ(SEARCH_ENGINE_ASK, GetEngineType("http://www.ask.com/"));
EXPECT_EQ(SEARCH_ENGINE_OTHER, GetEngineType("http://search.atlas.cz/"));
EXPECT_EQ(SEARCH_ENGINE_GOOGLE, GetEngineType("http://www.google.com/"));
}
TEST_F(TemplateURLPrepopulateDataTest, GetEngineTypeAdvanced) {
// Google URLs in different forms.
const char* kGoogleURLs[] = {
// Original with google:baseURL:
"{google:baseURL}search?q={searchTerms}&{google:RLZ}"
"{google:originalQueryForSuggestion}{google:searchFieldtrialParameter}"
"sourceid=chrome&ie={inputEncoding}",
// Custom with google.com and reordered query params:
"http://google.com/search?{google:RLZ}{google:originalQueryForSuggestion}"
"{google:searchFieldtrialParameter}"
"sourceid=chrome&ie={inputEncoding}&q={searchTerms}",
// Custom with a country TLD and almost no query params:
"http://www.google.ru/search?q={searchTerms}"
};
for (size_t i = 0; i < std::size(kGoogleURLs); ++i) {
EXPECT_EQ(SEARCH_ENGINE_GOOGLE, GetEngineType(kGoogleURLs[i]));
}
// Non-Google URLs.
const char* kYahooURLs[] = {
("http://search.yahoo.com/search?"
"ei={inputEncoding}&fr=crmas&p={searchTerms}"),
"http://search.yahoo.com/search?p={searchTerms}",
// Aggressively match types by checking just TLD+1.
"http://someothersite.yahoo.com/",
};
for (size_t i = 0; i < std::size(kYahooURLs); ++i) {
EXPECT_EQ(SEARCH_ENGINE_YAHOO, GetEngineType(kYahooURLs[i]));
}
// URLs for engines not present in country-specific lists.
EXPECT_EQ(SEARCH_ENGINE_NIGMA,
GetEngineType("http://nigma.ru/?s={searchTerms}&arg1=value1"));
// Also test matching against alternate URLs (and TLD+1 matching).
EXPECT_EQ(SEARCH_ENGINE_SOFTONIC,
GetEngineType("http://test.softonic.com.br/?{searchTerms}"));
// Search URL for which no prepopulated search provider exists.
EXPECT_EQ(SEARCH_ENGINE_OTHER,
GetEngineType("http://example.net/search?q={searchTerms}"));
EXPECT_EQ(SEARCH_ENGINE_OTHER, GetEngineType("invalid:search:url"));
// URL that doesn't look Google-related, but matches a Google base URL
// specified on the command line.
const std::string foo_url("http://www.foo.com/search?q={searchTerms}");
EXPECT_EQ(SEARCH_ENGINE_OTHER, GetEngineType(foo_url));
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kGoogleBaseURL, "http://www.foo.com/");
EXPECT_EQ(SEARCH_ENGINE_GOOGLE, GetEngineType(foo_url));
}
TEST_F(TemplateURLPrepopulateDataTest, GetEngineTypeForAllPrepopulatedEngines) {
using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
const std::vector<const PrepopulatedEngine*> all_engines =
TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
for (const PrepopulatedEngine* engine : all_engines) {
std::unique_ptr<TemplateURLData> data =
TemplateURLDataFromPrepopulatedEngine(*engine);
EXPECT_EQ(engine->type,
TemplateURL(*data).GetEngineType(SearchTermsData()));
}
}
TEST_F(TemplateURLPrepopulateDataTest, CheckSearchURLDetection) {
using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
const std::vector<const PrepopulatedEngine*> all_engines =
TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
for (const PrepopulatedEngine* engine : all_engines) {
std::unique_ptr<TemplateURLData> data =
TemplateURLDataFromPrepopulatedEngine(*engine);
TemplateURL t_url(*data);
SearchTermsData search_data;
// Test that search term is successfully extracted from generated search
// url.
GURL search_url = t_url.GenerateSearchURL(search_data);
EXPECT_TRUE(t_url.IsSearchURL(search_url, search_data))
<< "Search url is incorrectly detected for " << search_url;
}
}
TEST_F(TemplateURLPrepopulateDataTest, HttpsUrls) {
// Search engines that don't use HTTPS URLs.
// Since Chrome and the Internet are trying to transition from HTTP to HTTPS,
// please get approval from a PM before entering new HTTP exceptions here.
std::set<int> exceptions{
4, 6, 16, 17, 21, 27, 35, 36, 43, 44, 45, 50, 54, 55, 56, 60, 61,
62, 63, 64, 65, 66, 68, 70, 74, 75, 76, 77, 78, 79, 80, 81, 85, 90,
};
using PrepopulatedEngine = TemplateURLPrepopulateData::PrepopulatedEngine;
const std::vector<const PrepopulatedEngine*> all_engines =
TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
for (const PrepopulatedEngine* engine : all_engines) {
std::unique_ptr<TemplateURLData> data =
TemplateURLDataFromPrepopulatedEngine(*engine);
if (base::Contains(exceptions, data->prepopulate_id))
continue;
GURL logo_url = data->logo_url;
EXPECT_TRUE(logo_url.is_empty() || logo_url.SchemeIsCryptographic())
<< logo_url;
GURL doodle_url = data->doodle_url;
EXPECT_TRUE(doodle_url.is_empty() || doodle_url.SchemeIsCryptographic())
<< doodle_url;
EXPECT_TRUE(logo_url.is_empty() || doodle_url.is_empty())
<< "Only one of logo_url or doodle_url should be set.";
GURL favicon_url = data->favicon_url;
EXPECT_TRUE(favicon_url.is_empty() || favicon_url.SchemeIsCryptographic())
<< favicon_url;
TemplateURL template_url(*data);
// Intentionally don't check alternate URLs, because those are only used
// for matching.
CheckTemplateUrlRefIsCryptographic(template_url.url_ref());
CheckTemplateUrlRefIsCryptographic(template_url.suggestions_url_ref());
CheckTemplateUrlRefIsCryptographic(template_url.image_url_ref());
CheckTemplateUrlRefIsCryptographic(template_url.new_tab_url_ref());
CheckTemplateUrlRefIsCryptographic(
template_url.contextual_search_url_ref());
}
}
TEST_F(TemplateURLPrepopulateDataTest, FindGoogleIndex) {
constexpr int kGoogleId = 1;
size_t index;
std::vector<std::unique_ptr<TemplateURLData>> urls;
// Google is first in US, so confirm index 0.
OverrideCountryId(country_codes::CountryCharsToCountryID('U', 'S'));
urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &index);
EXPECT_EQ(index, size_t{0});
EXPECT_EQ(urls[index]->prepopulate_id, kGoogleId);
// Google is not first in CN; confirm it is found at index > 0.
// If Google ever does reach top in China, this test will need to be adjusted:
// check template_url_prepopulate_data.cc reference orders (engines_CN, etc.)
// to find a suitable country and index.
OverrideCountryId(country_codes::CountryCharsToCountryID('C', 'N'));
urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), &index);
EXPECT_GT(index, size_t{0});
EXPECT_LT(index, urls.size());
EXPECT_EQ(urls[index]->prepopulate_id, kGoogleId);
}
// Regression test for https://crbug.com/1500526.
TEST_F(TemplateURLPrepopulateDataTest, GetPrepopulatedEngineFromFullList) {
// Ensure that we use the default set of search engines, which is google,
// bing, yahoo.
OverrideCountryId(country_codes::kCountryIDUnknown);
ASSERT_EQ(TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), nullptr)
.size(),
3u);
// `GetPrepopulatedEngine()` only looks in the profile country's prepopulated
// list.
EXPECT_FALSE(TemplateURLPrepopulateData::GetPrepopulatedEngine(
&prefs_, search_engine_choice_service(),
TemplateURLPrepopulateData::ecosia.id));
// Here we look in the full list.
auto found_engine =
TemplateURLPrepopulateData::GetPrepopulatedEngineFromFullList(
&prefs_, search_engine_choice_service(),
TemplateURLPrepopulateData::ecosia.id);
EXPECT_TRUE(found_engine);
auto expected_engine =
TemplateURLDataFromPrepopulatedEngine(TemplateURLPrepopulateData::ecosia);
ExpectSimilar(expected_engine.get(), found_engine.get());
}
#if BUILDFLAG(IS_ANDROID)
TEST_F(TemplateURLPrepopulateDataTest, GetLocalPrepopulatedEngines) {
constexpr char sample_country[] = "US";
OverrideCountryId(country_codes::CountryCharsToCountryID(sample_country[0],
sample_country[1]));
// For a given country, the output from `GetLocalPrepopulatedEngines`
// should match the template URLs obtained from `GetPrepopulatedEngines`.
auto expected_urls = TemplateURLPrepopulateData::GetPrepopulatedEngines(
&prefs_, search_engine_choice_service(), nullptr);
auto actual_urls = TemplateURLPrepopulateData::GetLocalPrepopulatedEngines(
sample_country, prefs_);
ASSERT_EQ(actual_urls.size(), expected_urls.size());
for (unsigned int i = 0; i < actual_urls.size(); ++i) {
EXPECT_EQ(actual_urls[i]->prepopulate_id, expected_urls[i]->prepopulate_id);
EXPECT_EQ(actual_urls[i]->keyword(), expected_urls[i]->keyword());
EXPECT_EQ(actual_urls[i]->url(), expected_urls[i]->url());
}
EXPECT_THAT(TemplateURLPrepopulateData::GetLocalPrepopulatedEngines(
"NOT A COUNTRY", prefs_),
testing::IsEmpty());
}
#endif // BUILDFLAG(IS_ANDROID)