blob: 07cebef235f84746b25fd6970fe6c95b839ff483 [file] [log] [blame]
// Copyright 2012 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/omnibox/browser/autocomplete_provider.h"
#include <stddef.h>
#include <memory>
#include <string>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/base64url.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "base/test/values_test_util.h"
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/keyword_provider.h"
#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
#include "components/omnibox/browser/omnibox_prefs.h"
#include "components/omnibox/browser/omnibox_triggered_feature_service.h"
#include "components/omnibox/browser/search_provider.h"
#include "components/omnibox/browser/zero_suggest_provider.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/open_from_clipboard/fake_clipboard_recent_content.h"
#include "components/prefs/testing_pref_service.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/search_engines/template_url_service_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "third_party/metrics_proto/omnibox_focus_type.pb.h"
#include "third_party/omnibox_proto/types.pb.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/gfx/image/image_util.h"
#include "url/url_constants.h"
namespace {
const size_t kResultsPerProvider = 3;
const char16_t kTestTemplateURLKeyword[] = u"t";
class TestingSchemeClassifier : public AutocompleteSchemeClassifier {
public:
TestingSchemeClassifier() = default;
TestingSchemeClassifier(const TestingSchemeClassifier&) = delete;
TestingSchemeClassifier& operator=(const TestingSchemeClassifier&) = delete;
metrics::OmniboxInputType GetInputTypeForScheme(
const std::string& scheme) const override {
DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
return (scheme == url::kHttpScheme || scheme == url::kHttpsScheme)
? metrics::OmniboxInputType::URL
: metrics::OmniboxInputType::EMPTY;
}
};
// AutocompleteController::Observer implementation that runs the provided
// closure, when the controller is done.
class TestAutocompleteControllerObserver
: public AutocompleteController::Observer {
public:
TestAutocompleteControllerObserver() = default;
~TestAutocompleteControllerObserver() override = default;
TestAutocompleteControllerObserver(
const TestAutocompleteControllerObserver&) = delete;
TestAutocompleteControllerObserver& operator=(
const TestAutocompleteControllerObserver&) = delete;
void set_closure(const base::RepeatingClosure& closure) {
closure_ = closure;
}
void set_is_observing() { is_observing_ = true; }
bool is_observing() const { return is_observing_; }
private:
// AutocompleteController::Observer implementation.
void OnResultChanged(AutocompleteController* controller,
bool default_match_changed) override {
if (controller->done() && closure_) {
closure_.Run();
}
}
base::RepeatingClosure closure_;
bool is_observing_ = false;
};
// AutocompleteProviderListener implementation that runs the provided closure,
// when the provider is done. Informs the controller for non-prefetch requests.
class TestAutocompleteProviderListener : public AutocompleteProviderListener {
public:
explicit TestAutocompleteProviderListener(AutocompleteController* controller)
: controller_(controller) {}
~TestAutocompleteProviderListener() override = default;
TestAutocompleteProviderListener(const TestAutocompleteProviderListener&) =
delete;
TestAutocompleteProviderListener& operator=(
const TestAutocompleteProviderListener&) = delete;
void set_closure(const base::RepeatingClosure& closure) {
closure_ = closure;
}
// TestAutocompleteProviderListener:
void OnProviderUpdate(bool updated_matches,
const AutocompleteProvider* provider) override {
controller_->OnProviderUpdate(updated_matches, provider);
if (closure_)
closure_.Run();
}
// Used by TestProvider to notify it is done with a prefetch request.
void OnProviderFinishedPrefetch() {
if (closure_)
closure_.Run();
}
private:
raw_ptr<AutocompleteController> controller_;
base::RepeatingClosure closure_;
};
} // namespace
// Autocomplete provider that provides known results. Note that this is
// refcounted so that it can also be a task on the message loop.
class TestProvider : public AutocompleteProvider {
public:
TestProvider(int relevance,
const std::u16string& prefix,
const std::u16string& match_keyword,
AutocompleteProviderClient* client)
: AutocompleteProvider(AutocompleteProvider::TYPE_SEARCH),
relevance_(relevance),
prefix_(prefix),
match_keyword_(match_keyword),
client_(client) {}
TestProvider(const TestProvider&) = delete;
TestProvider& operator=(const TestProvider&) = delete;
// AutocompleteProvider:
void StartPrefetch(const AutocompleteInput& input) override;
void Start(const AutocompleteInput& input, bool minimal_changes) override;
void set_supports_prefetch(const bool supports_prefetch) {
supports_prefetch_ = supports_prefetch;
}
bool prefetch_done() { return prefetch_done_; }
void set_closure(const base::RepeatingClosure& closure) {
closure_ = closure;
}
protected:
~TestProvider() override = default;
void OnNonPrefetchRequestDone();
void OnPrefetchRequestDone();
void AddResults(int start_at, int num);
void AddResultsWithSearchTermsArgs(
int start_at,
int num,
AutocompleteMatch::Type type,
const TemplateURLRef::SearchTermsArgs& search_terms_args);
int relevance_;
const std::u16string prefix_;
const std::u16string match_keyword_;
raw_ptr<AutocompleteProviderClient> client_;
bool supports_prefetch_{false};
bool prefetch_done_{true};
base::RepeatingClosure closure_;
};
void TestProvider::StartPrefetch(const AutocompleteInput& input) {
if (!supports_prefetch_) {
return;
}
matches_.clear();
prefetch_done_ = false;
if (closure_) {
closure_.Run();
}
if (!input.omit_asynchronous_matches()) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&TestProvider::OnPrefetchRequestDone,
base::Unretained(this)));
} else {
OnPrefetchRequestDone();
}
}
void TestProvider::OnPrefetchRequestDone() {
AddResults(0, kResultsPerProvider);
prefetch_done_ = true;
for (auto* listener : listeners_) {
static_cast<TestAutocompleteProviderListener*>(listener)
->OnProviderFinishedPrefetch();
}
}
void TestProvider::Start(const AutocompleteInput& input, bool minimal_changes) {
if (minimal_changes)
return;
matches_.clear();
if (input.focus_type() != metrics::OmniboxFocusType::INTERACTION_DEFAULT)
return;
// Generate 4 results synchronously, the rest later.
AddResults(0, 1);
AddResultsWithSearchTermsArgs(1, 1,
AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
TemplateURLRef::SearchTermsArgs(u"echo"));
AddResultsWithSearchTermsArgs(2, 1, AutocompleteMatchType::NAVSUGGEST,
TemplateURLRef::SearchTermsArgs(u"nav"));
AddResultsWithSearchTermsArgs(3, 1, AutocompleteMatchType::SEARCH_SUGGEST,
TemplateURLRef::SearchTermsArgs(u"query"));
if (!input.omit_asynchronous_matches()) {
done_ = false;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&TestProvider::OnNonPrefetchRequestDone,
base::Unretained(this)));
}
}
void TestProvider::OnNonPrefetchRequestDone() {
AddResults(1, kResultsPerProvider);
done_ = true;
NotifyListeners(true);
}
void TestProvider::AddResults(int start_at, int num) {
AddResultsWithSearchTermsArgs(
start_at, num, AutocompleteMatchType::URL_WHAT_YOU_TYPED,
TemplateURLRef::SearchTermsArgs(std::u16string()));
}
void TestProvider::AddResultsWithSearchTermsArgs(
int start_at,
int num,
AutocompleteMatch::Type type,
const TemplateURLRef::SearchTermsArgs& search_terms_args) {
for (int i = start_at; i < num; i++) {
AutocompleteMatch match(this, relevance_ - i, false, type);
match.fill_into_edit = prefix_ + base::UTF8ToUTF16(base::NumberToString(i));
match.destination_url = GURL(base::UTF16ToUTF8(match.fill_into_edit));
match.allowed_to_be_default_match = true;
match.contents = match.fill_into_edit;
match.contents_class.push_back(
ACMatchClassification(0, ACMatchClassification::NONE));
match.description = match.fill_into_edit;
match.description_class.push_back(
ACMatchClassification(0, ACMatchClassification::NONE));
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(search_terms_args);
if (!match_keyword_.empty()) {
match.keyword = match_keyword_;
ASSERT_NE(nullptr,
match.GetTemplateURL(client_->GetTemplateURLService(), false));
}
matches_.push_back(match);
}
}
// Helper class to make running tests of ClassifyAllMatchesInString() more
// convenient.
class ClassifyTest {
public:
ClassifyTest(const std::u16string& text,
const bool text_is_query,
ACMatchClassifications matches);
~ClassifyTest();
ACMatchClassifications RunTest(const std::u16string& find_text);
private:
const std::u16string text_;
const bool text_is_query_;
const ACMatchClassifications matches_;
};
ClassifyTest::ClassifyTest(const std::u16string& text,
const bool text_is_query,
ACMatchClassifications matches)
: text_(text), text_is_query_(text_is_query), matches_(matches) {}
ClassifyTest::~ClassifyTest() = default;
ACMatchClassifications ClassifyTest::RunTest(const std::u16string& find_text) {
return AutocompleteProvider::ClassifyAllMatchesInString(
find_text, text_, text_is_query_, matches_);
}
class AutocompleteProviderTest : public testing::Test {
public:
AutocompleteProviderTest();
~AutocompleteProviderTest() override;
AutocompleteProviderTest(const AutocompleteProviderTest&) = delete;
AutocompleteProviderTest& operator=(const AutocompleteProviderTest&) = delete;
void CopyResults();
protected:
struct KeywordTestData {
const std::u16string fill_into_edit;
const std::u16string keyword;
const std::u16string expected_associated_keyword;
};
struct SuggestionGroupsTestData {
omnibox::GroupConfigMap suggestion_groups_map;
std::vector<absl::optional<omnibox::GroupId>> suggestion_group_ids;
};
struct AssistedQueryStatsTestData {
const AutocompleteMatch::Type match_type;
const std::string expected_aqs;
const metrics::ChromeSearchboxStats expected_searchbox_stats;
base::flat_set<omnibox::SuggestSubtype> subtypes;
};
// Registers a test TemplateURL under the given keyword.
void RegisterTemplateURL(const std::u16string& keyword,
const std::string& template_url,
const std::string& image_url,
const std::string& image_url_post_params);
// Resets |controller_| with two TestProviders. |provider1_ptr| and
// |provider2_ptr| are updated to point to the new providers if non-NULL.
void ResetControllerWithTestProviders(bool same_destinations,
TestProvider** provider1_ptr,
TestProvider** provider2_ptr);
// Runs a query on the input "a", and makes sure both providers' input is
// properly collected.
void RunTest();
// Constructs an AutocompleteResult from |match_data|, sets the |controller_|
// to pretend it was running against input |input|, calls the |controller_|'s
// UpdateAssociatedKeywords, and checks that the matches have associated
// keywords as expected.
void RunKeywordTest(const std::u16string& input,
const KeywordTestData* match_data,
size_t size);
void UpdateResultsWithSuggestionGroupsTestData(
const SuggestionGroupsTestData& test_data);
void RunAssistedQueryStatsTest(
const AssistedQueryStatsTestData* aqs_test_data,
size_t size);
void RunQuery(const std::string& query, bool allow_exact_keyword_match);
void ResetControllerWithKeywordAndSearchProviders();
void ResetControllerWithKeywordProvider();
void RunExactKeymatchTest(bool allow_exact_keyword_match);
// Returns match.destination_url as it would be set by
// AutocompleteController::UpdateMatchDestinationURL().
GURL GetDestinationURL(AutocompleteMatch& match,
base::TimeDelta query_formulation_time) const;
void set_remote_search_feature_triggered_in_session(bool value) {
client_->GetOmniboxTriggeredFeatureService()->ResetSession();
if (value) {
client_->GetOmniboxTriggeredFeatureService()->FeatureTriggered(
OmniboxTriggeredFeatureService::Feature::kRemoteSearchFeature);
}
}
void set_current_page_classification(
metrics::OmniboxEventProto::PageClassification classification) {
controller_->input_.current_page_classification_ = classification;
}
void add_zero_suggest_provider_experiment_stats_v2(
const metrics::ChromeSearchboxStats::ExperimentStatsV2&
experiment_stat_v2) {
auto& experiment_stats_v2s =
const_cast<SearchSuggestionParser::ExperimentStatsV2s&>(
controller_->zero_suggest_provider_->experiment_stats_v2s());
experiment_stats_v2s.push_back(experiment_stat_v2);
}
TestingPrefServiceSimple* GetPrefs() { return &pref_service_; }
// Resets the controller with the given |type|. |type| is a bitmap containing
// AutocompleteProvider::Type values that will (potentially, depending on
// platform, flags, etc.) be instantiated.
void ResetControllerWithType(int type);
AutocompleteResult result_;
base::test::TaskEnvironment task_environment_;
TestingPrefServiceSimple pref_service_;
TestAutocompleteControllerObserver autocomplete_controller_observer_;
std::unique_ptr<AutocompleteController> controller_;
// Owned by |controller_|.
raw_ptr<MockAutocompleteProviderClient> client_;
// Used to ensure that |client_| ownership has been passed to |controller_|
// exactly once.
bool client_owned_{};
};
AutocompleteProviderTest::AutocompleteProviderTest()
: client_(new MockAutocompleteProviderClient()) {
client_->set_template_url_service(
std::make_unique<TemplateURLService>(nullptr, 0));
}
AutocompleteProviderTest::~AutocompleteProviderTest() {
EXPECT_TRUE(client_owned_);
}
void AutocompleteProviderTest::RegisterTemplateURL(
const std::u16string& keyword,
const std::string& template_url,
const std::string& image_url = "",
const std::string& image_url_post_params = "") {
TemplateURLData data;
data.SetURL(template_url);
data.SetShortName(keyword);
data.SetKeyword(keyword);
data.image_url = image_url;
data.image_url_post_params = image_url_post_params;
TemplateURLService* turl_model = client_->GetTemplateURLService();
TemplateURL* default_turl =
turl_model->Add(std::make_unique<TemplateURL>(data));
turl_model->SetUserSelectedDefaultSearchProvider(default_turl);
turl_model->Load();
TemplateURLID default_provider_id = default_turl->id();
ASSERT_NE(0, default_provider_id);
}
void AutocompleteProviderTest::ResetControllerWithTestProviders(
bool same_destinations,
TestProvider** provider1_ptr,
TestProvider** provider2_ptr) {
// TODO: Move it outside this method, after refactoring the existing
// unit tests. Specifically:
// (1) Make sure that AutocompleteMatch.keyword is set iff there is
// a corresponding call to RegisterTemplateURL; otherwise the
// controller flow will crash; this practically means that
// RunTests/ResetControllerXXX/RegisterTemplateURL should
// be coordinated with each other.
// (2) Inject test arguments rather than rely on the hardcoded values, e.g.
// don't rely on kResultsPerProvided and default relevance ordering
// (B > A).
RegisterTemplateURL(kTestTemplateURLKeyword,
"http://aqs/{searchTerms}/{google:assistedQueryStats}");
AutocompleteController::Providers providers;
// Construct two new providers, with either the same or different prefixes.
TestProvider* provider1 = new TestProvider(kResultsPerProvider, u"http://a",
kTestTemplateURLKeyword, client_);
providers.push_back(provider1);
TestProvider* provider2 = new TestProvider(
kResultsPerProvider * 2, same_destinations ? u"http://a" : u"http://b",
std::u16string(), client_);
providers.push_back(provider2);
// Reset the controller to contain our new providers.
ResetControllerWithType(0);
// We're going to swap the providers vector, but the old vector should be
// empty so no elements need to be freed at this point.
EXPECT_TRUE(controller_->providers_.empty());
controller_->providers_.swap(providers);
provider1->AddListener(controller_.get());
provider2->AddListener(controller_.get());
if (provider1_ptr)
*provider1_ptr = provider1;
if (provider2_ptr)
*provider2_ptr = provider2;
}
void AutocompleteProviderTest::ResetControllerWithKeywordAndSearchProviders() {
// Reset the default TemplateURL.
TemplateURLData data;
data.SetShortName(u"default");
data.SetKeyword(u"default");
data.SetURL("http://defaultturl/{searchTerms}");
TemplateURLService* turl_model = client_->GetTemplateURLService();
TemplateURL* default_turl =
turl_model->Add(std::make_unique<TemplateURL>(data));
turl_model->SetUserSelectedDefaultSearchProvider(default_turl);
TemplateURLID default_provider_id = default_turl->id();
ASSERT_NE(0, default_provider_id);
// Create another TemplateURL for KeywordProvider.
TemplateURLData data2;
data2.SetShortName(u"k");
data2.SetKeyword(u"k");
data2.SetURL("http://keyword/{searchTerms}");
TemplateURL* keyword_turl =
turl_model->Add(std::make_unique<TemplateURL>(data2));
ASSERT_NE(0, keyword_turl->id());
ResetControllerWithType(AutocompleteProvider::TYPE_KEYWORD |
AutocompleteProvider::TYPE_SEARCH |
AutocompleteProvider::TYPE_ZERO_SUGGEST);
}
void AutocompleteProviderTest::ResetControllerWithKeywordProvider() {
TemplateURLService* turl_model = client_->GetTemplateURLService();
// Create a TemplateURL for KeywordProvider.
TemplateURLData data;
data.SetShortName(u"foo.com");
data.SetKeyword(u"foo.com");
data.SetURL("http://foo.com/{searchTerms}");
data.is_active = TemplateURLData::ActiveStatus::kTrue;
TemplateURL* keyword_turl =
turl_model->Add(std::make_unique<TemplateURL>(data));
ASSERT_NE(0, keyword_turl->id());
// Make a TemplateURL for KeywordProvider that a shorter version of the
// first.
data.SetShortName(u"f");
data.SetKeyword(u"f");
data.SetURL("http://f.com/{searchTerms}");
data.is_active = TemplateURLData::ActiveStatus::kTrue;
keyword_turl = turl_model->Add(std::make_unique<TemplateURL>(data));
ASSERT_NE(0, keyword_turl->id());
// Create another TemplateURL for KeywordProvider.
data.SetShortName(u"bar.com");
data.SetKeyword(u"bar.com");
data.SetURL("http://bar.com/{searchTerms}");
data.is_active = TemplateURLData::ActiveStatus::kTrue;
keyword_turl = turl_model->Add(std::make_unique<TemplateURL>(data));
ASSERT_NE(0, keyword_turl->id());
ResetControllerWithType(AutocompleteProvider::TYPE_KEYWORD);
}
void AutocompleteProviderTest::ResetControllerWithType(int type) {
EXPECT_FALSE(client_owned_);
controller_ = std::make_unique<AutocompleteController>(
base::WrapUnique(client_.get()), type);
client_owned_ = true;
}
void AutocompleteProviderTest::RunTest() {
RunQuery("a", true);
}
void AutocompleteProviderTest::RunKeywordTest(const std::u16string& input,
const KeywordTestData* match_data,
size_t size) {
ACMatches matches;
for (size_t i = 0; i < size; ++i) {
AutocompleteMatch match;
match.relevance = 1000; // Arbitrary non-zero value.
match.allowed_to_be_default_match = true;
match.fill_into_edit = match_data[i].fill_into_edit;
match.transition = ui::PAGE_TRANSITION_KEYWORD;
match.keyword = match_data[i].keyword;
matches.push_back(match);
}
AutocompleteInput autocomplete_input(
input,
metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
TestingSchemeClassifier());
autocomplete_input.set_prefer_keyword(true);
controller_->input_ = autocomplete_input;
AutocompleteResult result;
result.AppendMatches(matches);
controller_->UpdateAssociatedKeywords(&result);
for (size_t j = 0; j < result.size(); ++j) {
EXPECT_EQ(match_data[j].expected_associated_keyword,
result.match_at(j)->associated_keyword
? result.match_at(j)->associated_keyword->keyword
: std::u16string());
}
}
void AutocompleteProviderTest::UpdateResultsWithSuggestionGroupsTestData(
const SuggestionGroupsTestData& test_data) {
// Create new matches and add to the result.
size_t relevance = 1000;
ACMatches matches;
for (auto suggestion_group_id : test_data.suggestion_group_ids) {
AutocompleteMatch match(nullptr, relevance--, false,
AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED);
if (suggestion_group_id.has_value()) {
match.suggestion_group_id = suggestion_group_id.value();
}
matches.push_back(match);
}
result_.Reset();
result_.AppendMatches(matches);
// Update the result with the suggestion groups information.
result_.MergeSuggestionGroupsMap(test_data.suggestion_groups_map);
// Group matches with group IDs and move them to the bottom of the result set.
result_.GroupAndDemoteMatchesInGroups();
}
void AutocompleteProviderTest::RunAssistedQueryStatsTest(
const AssistedQueryStatsTestData* aqs_test_data,
size_t size) {
// Prepare input.
const size_t kMaxRelevance = 1000;
ACMatches matches;
for (size_t i = 0; i < size; ++i) {
AutocompleteMatch match(nullptr, kMaxRelevance - i, false,
aqs_test_data[i].match_type);
match.allowed_to_be_default_match = true;
match.keyword = kTestTemplateURLKeyword;
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(std::u16string());
match.subtypes = aqs_test_data[i].subtypes;
matches.push_back(match);
}
result_.Reset();
result_.AppendMatches(matches);
// Update AQS.
controller_->UpdateAssistedQueryStats(&result_);
// Verify data.
for (size_t i = 0; i < size; ++i) {
EXPECT_EQ(aqs_test_data[i].expected_aqs,
result_.match_at(i)->search_terms_args->assisted_query_stats);
std::string serialized_searchbox_stats;
result_.match_at(i)->search_terms_args->searchbox_stats.SerializeToString(
&serialized_searchbox_stats);
std::string encoded_searchbox_stats;
base::Base64UrlEncode(serialized_searchbox_stats,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_searchbox_stats);
std::string expected_serialized_searchbox_stats;
aqs_test_data[i].expected_searchbox_stats.SerializeToString(
&expected_serialized_searchbox_stats);
std::string expected_encoded_searchbox_stats;
base::Base64UrlEncode(expected_serialized_searchbox_stats,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&expected_encoded_searchbox_stats);
EXPECT_EQ(expected_encoded_searchbox_stats, encoded_searchbox_stats);
}
}
void AutocompleteProviderTest::RunQuery(const std::string& query,
bool allow_exact_keyword_match) {
result_.Reset();
AutocompleteInput input(base::ASCIIToUTF16(query),
metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
input.set_prevent_inline_autocomplete(true);
input.set_allow_exact_keyword_match(allow_exact_keyword_match);
base::RunLoop run_loop;
autocomplete_controller_observer_.set_closure(
run_loop.QuitClosure().Then(base::BindRepeating(
&AutocompleteProviderTest::CopyResults, base::Unretained(this))));
if (!autocomplete_controller_observer_.is_observing()) {
controller_->AddObserver(&autocomplete_controller_observer_);
autocomplete_controller_observer_.set_is_observing();
}
controller_->Start(input);
if (!controller_->done())
run_loop.Run();
}
void AutocompleteProviderTest::RunExactKeymatchTest(
bool allow_exact_keyword_match) {
// Send the controller input which exactly matches the keyword provider we
// created in ResetControllerWithKeywordAndSearchProviders(). The default
// match should thus be a search-other-engine match iff
// |allow_exact_keyword_match| is true. Regardless, the match should
// be from SearchProvider. (It provides all verbatim search matches,
// keyword or not.)
RunQuery("k test", allow_exact_keyword_match);
EXPECT_EQ(AutocompleteProvider::TYPE_SEARCH,
controller_->result().default_match()->provider->type());
EXPECT_EQ(allow_exact_keyword_match
? AutocompleteMatchType::SEARCH_OTHER_ENGINE
: AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
controller_->result().default_match()->type);
}
void AutocompleteProviderTest::CopyResults() {
result_.CopyFrom(controller_->result());
}
GURL AutocompleteProviderTest::GetDestinationURL(
AutocompleteMatch& match,
base::TimeDelta query_formulation_time) const {
controller_->UpdateMatchDestinationURLWithAdditionalAssistedQueryStats(
query_formulation_time, &match);
return match.destination_url;
}
// Tests that the default selection is set properly when updating results.
TEST_F(AutocompleteProviderTest, Query) {
TestProvider* provider1 = nullptr;
TestProvider* provider2 = nullptr;
ResetControllerWithTestProviders(false, &provider1, &provider2);
RunTest();
// Make sure the default match gets set to the highest relevance match. The
// highest relevance matches should come from the second provider.
EXPECT_EQ(
std::min(AutocompleteResult::GetMaxMatches(), kResultsPerProvider * 2),
result_.size());
ASSERT_TRUE(result_.default_match());
EXPECT_EQ(provider2, result_.default_match()->provider);
}
// Tests assisted query stats.
TEST_F(AutocompleteProviderTest, AssistedQueryStats) {
ResetControllerWithTestProviders(false, nullptr, nullptr);
RunTest();
ASSERT_EQ(
std::min(AutocompleteResult::GetMaxMatches(), kResultsPerProvider * 2),
result_.size());
// Now, check the results from the second provider, as they should not have
// assisted query stats set.
for (size_t i = 0; i < kResultsPerProvider; ++i) {
EXPECT_TRUE(
result_.match_at(i)->search_terms_args->assisted_query_stats.empty());
}
// The first provider has a test keyword, so AQS should be non-empty.
for (size_t i = kResultsPerProvider; i < result_.size(); ++i) {
EXPECT_FALSE(
result_.match_at(i)->search_terms_args->assisted_query_stats.empty());
}
}
TEST_F(AutocompleteProviderTest, RemoveDuplicates) {
TestProvider* provider1 = nullptr;
TestProvider* provider2 = nullptr;
ResetControllerWithTestProviders(true, &provider1, &provider2);
RunTest();
// Make sure all the first provider's results were eliminated by the second
// provider's.
EXPECT_EQ(kResultsPerProvider, result_.size());
for (AutocompleteResult::const_iterator i(result_.begin());
i != result_.end(); ++i)
EXPECT_EQ(provider2, i->provider);
}
TEST_F(AutocompleteProviderTest, AllowExactKeywordMatch) {
ResetControllerWithKeywordAndSearchProviders();
RunExactKeymatchTest(true);
RunExactKeymatchTest(false);
}
// Ensures matches from (only) the default search provider respect any extra
// query params set on the command line.
TEST_F(AutocompleteProviderTest, ExtraQueryParams) {
ResetControllerWithKeywordAndSearchProviders();
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kExtraSearchQueryParams, "a=b");
RunExactKeymatchTest(true);
CopyResults();
ASSERT_EQ(2U, result_.size());
EXPECT_EQ("http://keyword/test",
result_.match_at(0)->destination_url.possibly_invalid_spec());
EXPECT_EQ("http://defaultturl/k%20test?a=b",
result_.match_at(1)->destination_url.possibly_invalid_spec());
}
// Test that redundant associated keywords are removed.
TEST_F(AutocompleteProviderTest, RedundantKeywordsIgnoredInResult) {
ResetControllerWithKeywordProvider();
{
KeywordTestData duplicate_url[] = {
{u"fo", std::u16string(), std::u16string()},
{u"foo.com", std::u16string(), u"foo.com"},
{u"foo.com", std::u16string(), std::u16string()}};
SCOPED_TRACE("Duplicate url");
RunKeywordTest(u"fo", duplicate_url, std::size(duplicate_url));
}
{
KeywordTestData keyword_match[] = {
{u"foo.com", u"foo.com", std::u16string()},
{u"foo.com", std::u16string(), std::u16string()}};
SCOPED_TRACE("Duplicate url with keyword match");
RunKeywordTest(u"fo", keyword_match, std::size(keyword_match));
}
{
KeywordTestData multiple_keyword[] = {
{u"fo", std::u16string(), std::u16string()},
{u"foo.com", std::u16string(), u"foo.com"},
{u"foo.com", std::u16string(), std::u16string()},
{u"bar.com", std::u16string(), u"bar.com"},
};
SCOPED_TRACE("Duplicate url with multiple keywords");
RunKeywordTest(u"fo", multiple_keyword, std::size(multiple_keyword));
}
}
// Test that exact match keywords trump keywords associated with
// the match.
TEST_F(AutocompleteProviderTest, ExactMatchKeywords) {
ResetControllerWithKeywordProvider();
{
KeywordTestData keyword_match[] = {
{u"foo.com", std::u16string(), u"foo.com"}};
SCOPED_TRACE("keyword match as usual");
RunKeywordTest(u"fo", keyword_match, std::size(keyword_match));
}
// The same result set with an input of "f" (versus "fo") should get
// a different associated keyword because "f" is an exact match for
// a keyword and that should trump the keyword normally associated with
// this match.
{
KeywordTestData keyword_match[] = {{u"foo.com", std::u16string(), u"f"}};
SCOPED_TRACE("keyword exact match");
RunKeywordTest(u"f", keyword_match, std::size(keyword_match));
}
}
// Tests that the AutocompleteResult is updated with the suggestion group
// information and matches with group IDs are grouped and demoted correctly.
// Also verifies that:
// 1) headers are optional for suggestion groups.
// 2) suggestion groups are ordered based on their priories.
// 3) suggestion group IDs without associated suggestion group information are
// stripped away.
TEST_F(AutocompleteProviderTest, SuggestionGroups) {
ResetControllerWithKeywordAndSearchProviders();
const auto kRecommendedGroupId = omnibox::GROUP_PREVIOUS_SEARCH_RELATED;
const std::string kRecommended = "Recommended for you";
const auto kRecentSearchesGroupId =
omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS;
const std::string kRecentSearches = "Recent Searches";
const auto kBadGroupId = omnibox::GROUP_INVALID;
{
// AutocompleteResult::GetHeaderForSuggestionGroup() returns an empty string
// for unknown suggestion group IDs.
EXPECT_EQ(u"", result_.GetHeaderForSuggestionGroup(kBadGroupId));
// AutocompleteResult::IsSuggestionGroupHidden() returns false for unknown
// suggestion group IDs.
EXPECT_FALSE(result_.IsSuggestionGroupHidden(GetPrefs(), kBadGroupId));
// AutocompleteResult::SetSuggestionGroupHidden() does nothing for unknown
// suggestion group IDs.
result_.SetSuggestionGroupHidden(GetPrefs(), kBadGroupId, /*hidden=*/true);
EXPECT_FALSE(result_.IsSuggestionGroupHidden(GetPrefs(), kBadGroupId));
// AutocompleteResult::GetSectionForSuggestionGroup() returns
// omnibox::SECTION_DEFAULT for unknown suggestion group IDs.
EXPECT_EQ(omnibox::SECTION_DEFAULT,
result_.GetSectionForSuggestionGroup(kBadGroupId));
}
{
// Headers are optional for suggestion groups.
omnibox::GroupConfigMap suggestion_groups_map;
suggestion_groups_map[kRecommendedGroupId].set_header_text(kRecommended);
suggestion_groups_map[kRecommendedGroupId].set_section(
omnibox::SECTION_REMOTE_ZPS_4);
suggestion_groups_map[kRecentSearchesGroupId].set_section(
omnibox::SECTION_REMOTE_ZPS_3);
UpdateResultsWithSuggestionGroupsTestData({std::move(suggestion_groups_map),
{
{},
{},
{},
{kRecentSearchesGroupId},
{kRecommendedGroupId},
}});
EXPECT_FALSE(result_.match_at(0)->suggestion_group_id.has_value());
EXPECT_FALSE(result_.match_at(1)->suggestion_group_id.has_value());
EXPECT_FALSE(result_.match_at(2)->suggestion_group_id.has_value());
EXPECT_EQ(kRecentSearchesGroupId,
result_.match_at(3)->suggestion_group_id.value());
EXPECT_EQ(u"", result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId));
EXPECT_EQ(kRecommendedGroupId,
result_.match_at(4)->suggestion_group_id.value());
EXPECT_EQ(base::UTF8ToUTF16(kRecommended),
result_.GetHeaderForSuggestionGroup(kRecommendedGroupId));
}
{
// Suggestion groups are ordered based on their priories.
omnibox::GroupConfigMap suggestion_groups_map;
suggestion_groups_map[kRecommendedGroupId].set_header_text(kRecommended);
suggestion_groups_map[kRecommendedGroupId].set_section(
omnibox::SECTION_REMOTE_ZPS_3);
suggestion_groups_map[kRecentSearchesGroupId].set_header_text(
kRecentSearches);
suggestion_groups_map[kRecentSearchesGroupId].set_section(
omnibox::SECTION_REMOTE_ZPS_4);
UpdateResultsWithSuggestionGroupsTestData({std::move(suggestion_groups_map),
{
{},
{kRecentSearchesGroupId},
{},
{kRecommendedGroupId},
{kRecentSearchesGroupId},
}});
EXPECT_FALSE(result_.match_at(0)->suggestion_group_id.has_value());
EXPECT_FALSE(result_.match_at(1)->suggestion_group_id.has_value());
EXPECT_EQ(kRecommendedGroupId,
result_.match_at(2)->suggestion_group_id.value());
EXPECT_EQ(base::UTF8ToUTF16(kRecommended),
result_.GetHeaderForSuggestionGroup(kRecommendedGroupId));
EXPECT_EQ(kRecentSearchesGroupId,
result_.match_at(3)->suggestion_group_id.value());
EXPECT_EQ(base::UTF8ToUTF16(kRecentSearches),
result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId));
EXPECT_EQ(kRecentSearchesGroupId,
result_.match_at(4)->suggestion_group_id.value());
EXPECT_EQ(base::UTF8ToUTF16(kRecentSearches),
result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId));
}
{
// suggestion group IDs without associated suggestion group information are
// stripped away.
omnibox::GroupConfigMap suggestion_groups_map;
suggestion_groups_map[kRecommendedGroupId].set_header_text(kRecommended);
suggestion_groups_map[kRecentSearchesGroupId].set_header_text(
kRecentSearches);
UpdateResultsWithSuggestionGroupsTestData({std::move(suggestion_groups_map),
{
{kBadGroupId},
{kRecentSearchesGroupId},
{kRecommendedGroupId},
{kBadGroupId},
{kBadGroupId},
}});
EXPECT_FALSE(result_.match_at(0)->suggestion_group_id.has_value());
EXPECT_FALSE(result_.match_at(1)->suggestion_group_id.has_value());
EXPECT_FALSE(result_.match_at(2)->suggestion_group_id.has_value());
EXPECT_EQ(kRecentSearchesGroupId,
result_.match_at(3)->suggestion_group_id.value());
EXPECT_EQ(base::UTF8ToUTF16(kRecentSearches),
result_.GetHeaderForSuggestionGroup(kRecentSearchesGroupId));
EXPECT_EQ(kRecommendedGroupId,
result_.match_at(4)->suggestion_group_id.value());
EXPECT_EQ(base::UTF8ToUTF16(kRecommended),
result_.GetHeaderForSuggestionGroup(kRecommendedGroupId));
}
}
TEST_F(AutocompleteProviderTest, UpdateAssistedQueryStats) {
ResetControllerWithTestProviders(false, nullptr, nullptr);
{
metrics::ChromeSearchboxStats searchbox_stats;
AssistedQueryStatsTestData test_data[] = {
// MSVC doesn't support zero-length arrays, so supply some dummy data.
{AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "", searchbox_stats}};
SCOPED_TRACE("No matches");
// Note: We pass 0 here to ignore the dummy data above.
RunAssistedQueryStatsTest(test_data, 0);
}
// Note: See suggest.proto for the types and subtypes referenced below.
{
metrics::ChromeSearchboxStats searchbox_stats;
searchbox_stats.set_client_name("chrome");
searchbox_stats.set_num_zero_prefix_suggestions_shown(0);
searchbox_stats.set_zero_prefix_enabled(false);
auto* available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(0);
available_suggestion->set_type(omnibox::TYPE_NATIVE_CHROME);
available_suggestion->add_subtypes(omnibox::SUBTYPE_OMNIBOX_ECHO_SEARCH);
AssistedQueryStatsTestData test_data[] = {
{AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, "chrome..69i57",
searchbox_stats}};
SCOPED_TRACE("One match");
RunAssistedQueryStatsTest(test_data, std::size(test_data));
}
{
metrics::ChromeSearchboxStats searchbox_stats;
searchbox_stats.set_client_name("chrome");
searchbox_stats.set_num_zero_prefix_suggestions_shown(0);
searchbox_stats.set_zero_prefix_enabled(false);
auto* available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(0);
available_suggestion->set_type(omnibox::TYPE_ENTITY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_PERSONAL);
auto* assisted_query_info = searchbox_stats.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(0));
AssistedQueryStatsTestData test_data[] = {
{AutocompleteMatchType::SEARCH_SUGGEST_ENTITY,
"chrome.0.46i39",
searchbox_stats,
{omnibox::SUBTYPE_PERSONAL}}};
SCOPED_TRACE("One match with provider populated subtypes");
RunAssistedQueryStatsTest(test_data, std::size(test_data));
}
{
metrics::ChromeSearchboxStats searchbox_stats;
searchbox_stats.set_client_name("chrome");
searchbox_stats.set_num_zero_prefix_suggestions_shown(2);
searchbox_stats.set_zero_prefix_enabled(true);
auto* available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(0);
available_suggestion->set_type(omnibox::TYPE_QUERY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_PERSONAL);
available_suggestion->add_subtypes(omnibox::SUBTYPE_TRENDS);
available_suggestion->add_subtypes(omnibox::SUBTYPE_ZERO_PREFIX);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(1);
available_suggestion->set_type(omnibox::TYPE_ENTITY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_PERSONAL);
available_suggestion->add_subtypes(omnibox::SUBTYPE_TRENDS);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(2);
available_suggestion->set_type(omnibox::TYPE_ENTITY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_PERSONAL);
available_suggestion->add_subtypes(omnibox::SUBTYPE_TRENDS);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(3);
available_suggestion->set_type(omnibox::TYPE_ENTITY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_PERSONAL);
available_suggestion->add_subtypes(omnibox::SUBTYPE_TRENDS);
available_suggestion->add_subtypes(omnibox::SUBTYPE_ZERO_PREFIX);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(4);
available_suggestion->set_type(omnibox::TYPE_ENTITY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_PERSONAL);
available_suggestion->add_subtypes(omnibox::SUBTYPE_TRENDS);
metrics::ChromeSearchboxStats searchbox_stats_0;
searchbox_stats_0.MergeFrom(searchbox_stats);
auto* assisted_query_info = searchbox_stats_0.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(0));
metrics::ChromeSearchboxStats searchbox_stats_1;
searchbox_stats_1.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_1.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(1));
metrics::ChromeSearchboxStats searchbox_stats_2;
searchbox_stats_2.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_2.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(2));
metrics::ChromeSearchboxStats searchbox_stats_3;
searchbox_stats_3.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_3.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(3));
metrics::ChromeSearchboxStats searchbox_stats_4;
searchbox_stats_4.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_4.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(4));
// This test confirms that repetitive subtype information is being
// properly handled and reported as the same suggestion type.
AssistedQueryStatsTestData test_data[] = {
{AutocompleteMatchType::SEARCH_SUGGEST,
"chrome.0.0i39i143i362j46i39i143l2j46i39i143i362j46i39i143",
searchbox_stats_0,
{omnibox::SUBTYPE_PERSONAL, omnibox::SUBTYPE_TRENDS,
omnibox::SUBTYPE_ZERO_PREFIX, omnibox::SUBTYPE_TRENDS}},
// The next two matches should be detected as the same type, despite
// repeated subtype match.
{AutocompleteMatchType::SEARCH_SUGGEST_ENTITY,
"chrome.1.0i39i143i362j46i39i143l2j46i39i143i362j46i39i143",
searchbox_stats_1,
{omnibox::SUBTYPE_PERSONAL, omnibox::SUBTYPE_TRENDS}},
{AutocompleteMatchType::SEARCH_SUGGEST_ENTITY,
"chrome.2.0i39i143i362j46i39i143l2j46i39i143i362j46i39i143",
searchbox_stats_2,
{omnibox::SUBTYPE_PERSONAL, omnibox::SUBTYPE_TRENDS,
omnibox::SUBTYPE_PERSONAL}},
// This match should not be bundled together with previous two, because
// it comes with additional subtype information (42).
{AutocompleteMatchType::SEARCH_SUGGEST_ENTITY,
"chrome.3.0i39i143i362j46i39i143l2j46i39i143i362j46i39i143",
searchbox_stats_3,
{omnibox::SUBTYPE_PERSONAL, omnibox::SUBTYPE_TRENDS,
omnibox::SUBTYPE_ZERO_PREFIX}},
// This match should not be bundled together with the group before,
// because these items are not adjacent.
{AutocompleteMatchType::SEARCH_SUGGEST_ENTITY,
"chrome.4.0i39i143i362j46i39i143l2j46i39i143i362j46i39i143",
searchbox_stats_4,
{omnibox::SUBTYPE_PERSONAL, omnibox::SUBTYPE_TRENDS}},
};
SCOPED_TRACE("Complex set of matches with repetitive subtypes");
RunAssistedQueryStatsTest(test_data, std::size(test_data));
}
// This test confirms that selection of trivial suggestions does not get
// reported in `assisted_query_info`. And that the count of zero-prefix
// matches coming from the suggest server or the local device are recorded.
{
metrics::ChromeSearchboxStats searchbox_stats;
searchbox_stats.set_client_name("chrome");
searchbox_stats.set_num_zero_prefix_suggestions_shown(3);
searchbox_stats.set_zero_prefix_enabled(true);
auto* available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(0);
available_suggestion->set_type(omnibox::TYPE_NATIVE_CHROME);
available_suggestion->add_subtypes(omnibox::SUBTYPE_OMNIBOX_ECHO_SEARCH);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(1);
available_suggestion->set_type(omnibox::TYPE_NATIVE_CHROME);
available_suggestion->add_subtypes(omnibox::SUBTYPE_OMNIBOX_ECHO_URL);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(2);
available_suggestion->set_type(omnibox::TYPE_NAVIGATION);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(3);
available_suggestion->set_type(omnibox::TYPE_NAVIGATION);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(4);
available_suggestion->set_type(omnibox::TYPE_QUERY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_ZERO_PREFIX);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(5);
available_suggestion->set_type(omnibox::TYPE_QUERY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_ZERO_PREFIX);
available_suggestion->add_subtypes(
omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_HISTORY);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(6);
available_suggestion->set_type(omnibox::TYPE_QUERY);
available_suggestion->add_subtypes(omnibox::SUBTYPE_ZERO_PREFIX);
available_suggestion->add_subtypes(
omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS);
available_suggestion = searchbox_stats.add_available_suggestions();
available_suggestion->set_index(7);
available_suggestion->set_type(omnibox::TYPE_NATIVE_CHROME);
available_suggestion->add_subtypes(omnibox::SUBTYPE_OMNIBOX_HISTORY_SEARCH);
metrics::ChromeSearchboxStats searchbox_stats_0;
searchbox_stats_0.MergeFrom(searchbox_stats);
metrics::ChromeSearchboxStats searchbox_stats_1;
searchbox_stats_1.MergeFrom(searchbox_stats);
metrics::ChromeSearchboxStats searchbox_stats_2;
searchbox_stats_2.MergeFrom(searchbox_stats);
auto* assisted_query_info = searchbox_stats_2.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(2));
metrics::ChromeSearchboxStats searchbox_stats_3;
searchbox_stats_3.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_3.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(3));
metrics::ChromeSearchboxStats searchbox_stats_4;
searchbox_stats_4.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_4.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(4));
metrics::ChromeSearchboxStats searchbox_stats_5;
searchbox_stats_5.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_5.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(5));
metrics::ChromeSearchboxStats searchbox_stats_6;
searchbox_stats_6.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_6.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(6));
metrics::ChromeSearchboxStats searchbox_stats_7;
searchbox_stats_7.MergeFrom(searchbox_stats);
assisted_query_info = searchbox_stats_7.mutable_assisted_query_info();
assisted_query_info->MergeFrom(searchbox_stats.available_suggestions(7));
AssistedQueryStatsTestData test_data[] = {
{AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
"chrome..69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_0},
{AutocompleteMatchType::URL_WHAT_YOU_TYPED,
"chrome..69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_1},
{AutocompleteMatchType::NAVSUGGEST,
"chrome.2.69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_2},
{AutocompleteMatchType::NAVSUGGEST,
"chrome.3.69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_3},
{AutocompleteMatchType::SEARCH_SUGGEST,
"chrome.4.69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_4,
{omnibox::SUBTYPE_ZERO_PREFIX}},
{AutocompleteMatchType::SEARCH_SUGGEST,
"chrome.5.69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_5,
{omnibox::SUBTYPE_ZERO_PREFIX,
omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_HISTORY}},
{AutocompleteMatchType::SEARCH_SUGGEST,
"chrome.6.69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_6,
{omnibox::SUBTYPE_ZERO_PREFIX,
omnibox::SUBTYPE_ZERO_PREFIX_LOCAL_FREQUENT_URLS}},
{AutocompleteMatchType::SEARCH_HISTORY,
"chrome.7.69i57j69i58j5l2j0i362j0i362i450j0i362i451j69i59",
searchbox_stats_7},
};
SCOPED_TRACE("Trivial and zero-prefix matches");
RunAssistedQueryStatsTest(test_data, std::size(test_data));
}
}
TEST_F(AutocompleteProviderTest, GetDestinationURL_AssistedQueryStatsOnly) {
ResetControllerWithKeywordAndSearchProviders();
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({omnibox::kReportAssistedQueryStats},
{omnibox::kReportSearchboxStats});
// For the destination URL to have aqs parameters for query formulation time
// and the field trial triggered bit, many conditions need to be satisfied.
AutocompleteMatch match(nullptr, 1100, false,
AutocompleteMatchType::SEARCH_SUGGEST);
GURL url(GetDestinationURL(match, base::Milliseconds(2456)));
EXPECT_TRUE(url.path().empty());
// The protocol needs to be https.
RegisterTemplateURL(kTestTemplateURLKeyword,
"https://aqs/{searchTerms}/{google:assistedQueryStats}");
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// There needs to be a keyword provider.
match.keyword = kTestTemplateURLKeyword;
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// search_terms_args needs to be set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(std::u16string());
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// Both assisted_query_stats and searchbox_stats need to have been set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
match.search_terms_args->searchbox_stats.set_client_name("chrome");
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j0j0&", url.path());
// Test field trial triggered bit set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
set_remote_search_feature_triggered_in_session(true);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j0&", url.path());
// Test page classification set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
set_current_page_classification(metrics::OmniboxEventProto::OTHER);
set_remote_search_feature_triggered_in_session(false);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j0j4&", url.path());
// Test page classification and field trial triggered set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
set_remote_search_feature_triggered_in_session(true);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j4&", url.path());
// Test experiment stats set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
metrics::ChromeSearchboxStats::ExperimentStatsV2 experiment_stats_v2;
experiment_stats_v2.set_type_int(10001);
experiment_stats_v2.set_string_value("0:67");
add_zero_suggest_provider_experiment_stats_v2(experiment_stats_v2);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j4.10001i0,67&",
url.path());
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
experiment_stats_v2.set_type_int(10001);
experiment_stats_v2.set_string_value("54:67");
add_zero_suggest_provider_experiment_stats_v2(experiment_stats_v2);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ(
"//"
"aqs=chrome.0.69i57j69i58j5l2j0l3j69i59.2456j1j4.10001i0,67j10001i54,67&",
url.path());
}
TEST_F(AutocompleteProviderTest, GetDestinationURL_SearchboxStatsOnly) {
ResetControllerWithKeywordAndSearchProviders();
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({omnibox::kReportSearchboxStats},
{omnibox::kReportAssistedQueryStats});
// For the destination URL to have aqs parameters for query formulation time
// and the field trial triggered bit, many conditions need to be satisfied.
AutocompleteMatch match(nullptr, 1100, false,
AutocompleteMatchType::SEARCH_SUGGEST);
GURL url(GetDestinationURL(match, base::Milliseconds(2456)));
EXPECT_TRUE(url.path().empty());
// The protocol needs to be https.
RegisterTemplateURL(kTestTemplateURLKeyword,
"https://foo/{searchTerms}/{google:assistedQueryStats}");
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// There needs to be a keyword provider.
match.keyword = kTestTemplateURLKeyword;
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// search_terms_args needs to be set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(std::u16string());
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// Both assisted_query_stats and searchbox_stats need to have been set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
match.search_terms_args->searchbox_stats.set_client_name("chrome");
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//gs_lcrp=EgZjaHJvbWXSAQgyNDU2ajBqMA&", url.path());
// Make sure searchbox_stats is serialized and encoded correctly.
{
std::string serialized_proto;
EXPECT_TRUE(base::Base64UrlDecode(
"EgZjaHJvbWXSAQgyNDU2ajBqMA",
base::Base64UrlDecodePolicy::DISALLOW_PADDING, &serialized_proto));
metrics::ChromeSearchboxStats expected;
expected.ParseFromString(serialized_proto);
EXPECT_EQ("chrome", expected.client_name());
}
// Test field trial triggered bit set.
set_remote_search_feature_triggered_in_session(true);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//gs_lcrp=EgZjaHJvbWXSAQgyNDU2ajFqMA&", url.path());
// Make sure searchbox_stats is serialized and encoded correctly.
{
std::string serialized_proto;
EXPECT_TRUE(base::Base64UrlDecode(
"EgZjaHJvbWXSAQgyNDU2ajFqMA",
base::Base64UrlDecodePolicy::DISALLOW_PADDING, &serialized_proto));
metrics::ChromeSearchboxStats expected;
expected.ParseFromString(serialized_proto);
EXPECT_EQ("2456j1j0", expected.experiment_stats());
}
// Test page classification set.
set_remote_search_feature_triggered_in_session(false);
set_current_page_classification(metrics::OmniboxEventProto::OTHER);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//gs_lcrp=EgZjaHJvbWXSAQgyNDU2ajBqNA&", url.path());
// Make sure searchbox_stats is serialized and encoded correctly.
{
std::string serialized_proto;
EXPECT_TRUE(base::Base64UrlDecode(
"EgZjaHJvbWXSAQgyNDU2ajBqNA",
base::Base64UrlDecodePolicy::DISALLOW_PADDING, &serialized_proto));
metrics::ChromeSearchboxStats expected;
expected.ParseFromString(serialized_proto);
EXPECT_EQ("2456j0j4", expected.experiment_stats());
}
// Test page classification and field trial triggered set.
set_remote_search_feature_triggered_in_session(true);
set_current_page_classification(metrics::OmniboxEventProto::OTHER);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//gs_lcrp=EgZjaHJvbWXSAQgyNDU2ajFqNA&", url.path());
// Make sure searchbox_stats is serialized and encoded correctly.
{
std::string serialized_proto;
EXPECT_TRUE(base::Base64UrlDecode(
"EgZjaHJvbWXSAQgyNDU2ajFqNA",
base::Base64UrlDecodePolicy::DISALLOW_PADDING, &serialized_proto));
metrics::ChromeSearchboxStats expected;
expected.ParseFromString(serialized_proto);
EXPECT_EQ("2456j1j4", expected.experiment_stats());
}
// Test experiment stats v2 set.
metrics::ChromeSearchboxStats::ExperimentStatsV2 experiment_stats_v2;
experiment_stats_v2.set_type_int(10001);
experiment_stats_v2.set_string_value("0:67");
add_zero_suggest_provider_experiment_stats_v2(experiment_stats_v2);
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ("//gs_lcrp=EgZjaHJvbWXSAQgyNDU2ajFqNOIDCRIEMCw2NyCRTg&",
url.path());
// Make sure searchbox_stats is serialized and encoded correctly.
{
std::string serialized_proto;
EXPECT_TRUE(base::Base64UrlDecode(
"EgZjaHJvbWXSAQgyNDU2ajFqNOIDCRIEMCw2NyCRTg",
base::Base64UrlDecodePolicy::DISALLOW_PADDING, &serialized_proto));
metrics::ChromeSearchboxStats expected;
expected.ParseFromString(serialized_proto);
EXPECT_EQ(1, expected.experiment_stats_v2_size());
EXPECT_EQ(10001, expected.experiment_stats_v2(0).type_int());
EXPECT_EQ("0,67", expected.experiment_stats_v2(0).string_value());
}
}
TEST_F(AutocompleteProviderTest,
GetDestinationURL_AssistedQueryStatsAndSearchboxStats) {
ResetControllerWithKeywordAndSearchProviders();
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{omnibox::kReportSearchboxStats, omnibox::kReportAssistedQueryStats}, {});
// For the destination URL to have aqs parameters for query formulation time
// and the field trial triggered bit, many conditions need to be satisfied.
AutocompleteMatch match(nullptr, 1100, false,
AutocompleteMatchType::SEARCH_SUGGEST);
GURL url(GetDestinationURL(match, base::Milliseconds(2456)));
EXPECT_TRUE(url.path().empty());
// The protocol needs to be https.
RegisterTemplateURL(kTestTemplateURLKeyword,
"https://foo/{searchTerms}/{google:assistedQueryStats}");
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// There needs to be a keyword provider.
match.keyword = kTestTemplateURLKeyword;
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// search_terms_args needs to be set.
match.search_terms_args =
std::make_unique<TemplateURLRef::SearchTermsArgs>(std::u16string());
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// If assisted_query_stats is not set, searchbox_stats is not reported either.
match.search_terms_args->searchbox_stats.set_client_name("chrome");
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_TRUE(url.path().empty());
// Both assisted_query_stats and searchbox_stats need to have been set.
match.search_terms_args->assisted_query_stats =
"chrome.0.69i57j69i58j5l2j0l3j69i59";
url = GetDestinationURL(match, base::Milliseconds(2456));
EXPECT_EQ(
"//"
"gs_lcrp=EgZjaHJvbWXSAQgyNDU2ajBqMA&aqs=chrome.0."
"69i57j69i58j5l2j0l3j69i59.2456j0j0&",
url.path());
}
TEST_F(AutocompleteProviderTest, ClassifyAllMatchesInString) {
ResetControllerWithKeywordAndSearchProviders();
ACMatchClassifications matches =
AutocompleteMatch::ClassificationsFromString("0,0");
ClassifyTest classify_test(u"A man, a plan, a canal Panama",
/*text_is_query=*/false, matches);
ACMatchClassifications spans;
spans = classify_test.RunTest(u"man");
// A man, a plan, a canal Panama
// ACMatch spans should be: '--MMM------------------------'
EXPECT_EQ("0,0,2,2,5,0", AutocompleteMatch::ClassificationsToString(spans));
spans = classify_test.RunTest(u"man p");
// A man, a plan, a canal Panama
// ACMatch spans should be: '--MMM----M-------------M-----'
EXPECT_EQ("0,0,2,2,5,0,9,2,10,0,23,2,24,0",
AutocompleteMatch::ClassificationsToString(spans));
// Comparisons should be case insensitive.
spans = classify_test.RunTest(u"mAn pLAn panAMa");
// A man, a plan, a canal Panama
// ACMatch spans should be: '--MMM----MMMM----------MMMMMM'
EXPECT_EQ("0,0,2,2,5,0,9,2,13,0,23,2",
AutocompleteMatch::ClassificationsToString(spans));
// When the user input is a prefix of the suggest text, subsequent occurrences
// of the user words should be matched.
spans = classify_test.RunTest(u"a man,");
// A man, a plan, a canal Panama
// ACMatch spans should be: 'MMMMMM-----------------------'
EXPECT_EQ("0,2,6,0", AutocompleteMatch::ClassificationsToString(spans));
// Matches must begin at word-starts in the suggest text. E.g. 'a' in 'plan'
// should not match.
spans = classify_test.RunTest(u"a man, p");
// A man, a plan, a canal Panama
// ACMatch spans should be: 'M-MMM--M-M-----M-------M-----'
EXPECT_EQ("0,2,1,0,2,2,5,0,7,2,8,0,9,2,10,0,15,2,16,0,23,2,24,0",
AutocompleteMatch::ClassificationsToString(spans));
ClassifyTest classify_test2(
u"Yahoo! Sports - Sports News, "
u"Scores, Rumors, Fantasy Games, and more",
/*text_is_query=*/false, matches);
spans = classify_test2.RunTest(u"ne");
// Yahoo! Sports - Sports News, Scores, Rumors, Fantasy Games, and more
// -----------------------MM-------------------------------------------
EXPECT_EQ("0,0,23,2,25,0", AutocompleteMatch::ClassificationsToString(spans));
spans = classify_test2.RunTest(u"neWs R");
// Yahoo! Sports - Sports News, Scores, Rumors, Fantasy Games, and more
// -----------------------MMMM----------M------------------------------
EXPECT_EQ("0,0,23,2,27,0,37,2,38,0",
AutocompleteMatch::ClassificationsToString(spans));
matches = AutocompleteMatch::ClassificationsFromString("0,1");
ClassifyTest classify_test3(u"livescore.goal.com",
/*text_is_query=*/false, matches);
// Matches should be merged with existing classifications.
// Matches can begin after symbols in the suggest text.
spans = classify_test3.RunTest(u"go");
// livescore.goal.com
// ----------MM------
// ACMatch spans should match first two letters of the "goal".
EXPECT_EQ("0,1,10,3,12,1", AutocompleteMatch::ClassificationsToString(spans));
matches = AutocompleteMatch::ClassificationsFromString("0,0,13,1");
ClassifyTest classify_test4(u"Email login: mail.somecorp.com",
/*text_is_query=*/false, matches);
// Matches must begin at word-starts in the suggest text.
spans = classify_test4.RunTest(u"ail");
// Email login: mail.somecorp.com
// 000000000000011111111111111111
EXPECT_EQ("0,0,13,1", AutocompleteMatch::ClassificationsToString(spans));
// The longest matches should take precedence (e.g. 'log' instead of 'lo').
spans = classify_test4.RunTest(u"lo log mail em");
// Email login: mail.somecorp.com
// 220000222000033331111111111111
EXPECT_EQ("0,2,2,0,6,2,9,0,13,3,17,1",
AutocompleteMatch::ClassificationsToString(spans));
// Some web sites do not have a description. If the string being searched is
// empty, the classifications must also be empty: http://crbug.com/148647
// Extra parens in the next line hack around C++03's "most vexing parse".
class ClassifyTest classify_test5((std::u16string()), /*text_is_query=*/false,
ACMatchClassifications());
spans = classify_test5.RunTest(u"man");
ASSERT_EQ(0U, spans.size());
// Matches which end at beginning of classification merge properly.
matches = AutocompleteMatch::ClassificationsFromString("0,4,9,0");
ClassifyTest classify_test6(u"html password example",
/*text_is_query=*/false, matches);
// Extra space in the next string avoids having the string be a prefix of the
// text above, which would allow for two different valid classification sets,
// one of which uses two spans (the first of which would mark all of "html
// pass" as a match) and one which uses four (which marks the individual words
// as matches but not the space between them). This way only the latter is
// valid.
spans = classify_test6.RunTest(u"html pass");
EXPECT_EQ("0,6,4,4,5,6,9,0",
AutocompleteMatch::ClassificationsToString(spans));
// Multiple matches with both beginning and end at beginning of
// classifications merge properly.
matches = AutocompleteMatch::ClassificationsFromString("0,1,11,0");
ClassifyTest classify_test7(u"http://a.co is great",
/*text_is_query=*/false, matches);
spans = classify_test7.RunTest(u"ht co");
EXPECT_EQ("0,3,2,1,9,3,11,0",
AutocompleteMatch::ClassificationsToString(spans));
// Search queries should be bold non-matches and unbold matches.
matches = AutocompleteMatch::ClassificationsFromString("0,0");
ClassifyTest classify_test8(u"panama canal",
/*text_is_query=*/true, matches);
spans = classify_test8.RunTest(u"pan");
// panama canal
// ACMatch spans should be: "---MMMMMMMMM";
EXPECT_EQ("0,0,3,2", AutocompleteMatch::ClassificationsToString(spans));
spans = classify_test8.RunTest(u"canal");
// panama canal
// ACMatch spans should be: "MMMMMMM-----";
EXPECT_EQ("0,2,7,0", AutocompleteMatch::ClassificationsToString(spans));
// Search autocomplete suggestion.
ClassifyTest classify_test9(u"comcast webmail login",
/*text_is_query=*/true, ACMatchClassifications());
// Matches first and first part of middle word and the last word.
spans = classify_test9.RunTest(u"comcast web login");
// comcast webmail login
// ACMatch spans should be: "-------M---MMMMM-----";
EXPECT_EQ("0,0,7,2,8,0,11,2,16,0",
AutocompleteMatch::ClassificationsToString(spans));
// Matches partial word in the middle of suggestion.
spans = classify_test9.RunTest(u"web");
// comcast webmail login
// ACMatch spans should be: "MMMMMMMM---MMMMMMMMMM";
EXPECT_EQ("0,2,8,0,11,2", AutocompleteMatch::ClassificationsToString(spans));
ClassifyTest classify_test10(u"comcast.net web mail login",
/*text_is_query=*/true,
ACMatchClassifications());
spans = classify_test10.RunTest(u"comcast web login");
// comcast.net web mail login
// ACMatch spans should be: "-------MMMMM---MMMMMM-----";
EXPECT_EQ("0,0,7,2,12,0,15,2,21,0",
AutocompleteMatch::ClassificationsToString(spans));
// Same with |classify_test10| except using characters in
// base::kWhitespaceASCIIAs16 instead of white space.
ClassifyTest classify_test11(u"comcast.net\x0aweb\x0dmail login",
/*text_is_query=*/true,
ACMatchClassifications());
spans = classify_test11.RunTest(u"comcast web login");
// comcast.net web mail login
// ACMatch spans should be: "-------MMMMM---MMMMMM-----";
EXPECT_EQ("0,0,7,2,12,0,15,2,21,0",
AutocompleteMatch::ClassificationsToString(spans));
}
class AutocompleteProviderPrefetchTest : public AutocompleteProviderTest {
public:
AutocompleteProviderPrefetchTest() {
RegisterTemplateURL(kTestTemplateURLKeyword,
"http://aqs/{searchTerms}/{google:assistedQueryStats}");
// Create an empty controller.
ResetControllerWithType(0);
provider_listener_ =
std::make_unique<TestAutocompleteProviderListener>(controller_.get());
}
~AutocompleteProviderPrefetchTest() override = default;
AutocompleteProviderPrefetchTest(const AutocompleteProviderPrefetchTest&) =
delete;
AutocompleteProviderPrefetchTest& operator=(
const AutocompleteProviderPrefetchTest&) = delete;
protected:
std::unique_ptr<TestAutocompleteProviderListener> provider_listener_;
};
TEST_F(AutocompleteProviderPrefetchTest, SupportedProvider_NonPrefetch) {
// Add a test provider that supports prefetch requests.
TestProvider* provider = new TestProvider(kResultsPerProvider, u"http://a",
kTestTemplateURLKeyword, client_);
provider->set_supports_prefetch(true);
controller_->providers_.push_back(provider);
base::RunLoop listener_run_loop;
provider_listener_->set_closure(
listener_run_loop.QuitClosure().Then(base::BindRepeating(
&AutocompleteProviderTest::CopyResults, base::Unretained(this))));
provider->AddListener(provider_listener_.get());
AutocompleteInput input(u"foo", metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
controller_->Start(input);
ASSERT_FALSE(provider->done());
ASSERT_FALSE(controller_->done());
// Wait for the provider to finish asynchronously.
listener_run_loop.Run();
ASSERT_TRUE(provider->done());
ASSERT_TRUE(controller_->done());
// The results are expected to be non-empty as the provider did notify the
// controller of the non-prefetch request results.
EXPECT_EQ(kResultsPerProvider, result_.size());
}
TEST_F(AutocompleteProviderPrefetchTest, SupportedProvider_Prefetch) {
// Add a test provider that supports prefetch requests.
TestProvider* provider = new TestProvider(kResultsPerProvider, u"http://a",
kTestTemplateURLKeyword, client_);
provider->set_supports_prefetch(true);
controller_->providers_.push_back(provider);
base::RunLoop provider_run_loop;
provider->set_closure(provider_run_loop.QuitClosure());
base::RunLoop listener_run_loop;
provider_listener_->set_closure(
listener_run_loop.QuitClosure().Then(base::BindRepeating(
&AutocompleteProviderTest::CopyResults, base::Unretained(this))));
provider->AddListener(provider_listener_.get());
AutocompleteInput input(u"", metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
controller_->StartPrefetch(input);
// Wait for StartPrefetch() to be called on the provider.
provider_run_loop.Run();
// StartPrefetch() doesn't affect the state of the provider or the controller.
ASSERT_FALSE(provider->prefetch_done());
ASSERT_TRUE(provider->done());
ASSERT_TRUE(controller_->done());
// Wait for the provider to finish asynchronously.
listener_run_loop.Run();
ASSERT_TRUE(provider->prefetch_done());
ASSERT_TRUE(controller_->done());
// The results are expected to be empty as the provider did not notify the
// controller of the prefetch request results.
EXPECT_TRUE(result_.empty());
}
TEST_F(AutocompleteProviderPrefetchTest, SupportedProvider_OngoingNonPrefetch) {
// Add a test provider that supports prefetch requests.
TestProvider* provider = new TestProvider(kResultsPerProvider, u"http://a",
kTestTemplateURLKeyword, client_);
provider->set_supports_prefetch(true);
controller_->providers_.push_back(provider);
base::RunLoop provider_run_loop;
provider->set_closure(provider_run_loop.QuitClosure());
base::RunLoop listener_run_loop;
provider_listener_->set_closure(
listener_run_loop.QuitClosure().Then(base::BindRepeating(
&AutocompleteProviderTest::CopyResults, base::Unretained(this))));
provider->AddListener(provider_listener_.get());
AutocompleteInput input(u"bar", metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
controller_->Start(input);
ASSERT_TRUE(provider->prefetch_done());
ASSERT_FALSE(provider->done());
ASSERT_FALSE(controller_->done());
// Try to start a prefetch request while a non-prefetch request is still in
// progress. We expect this not to call StartPrefetch() on the provider.
// We test this by requesting synchronous matches from TestPrefetchProvider
// which notifies the provider listener synchronously and calls the quit
// closure of `run_loop` before `run_loop` is run. This prevents the provider
// from being able to notify the controller of finishing the non-prefetch
// request resulting in the controller to remain in an invalid state.
input.set_omit_asynchronous_matches(true);
controller_->StartPrefetch(input);
ASSERT_FALSE(provider_run_loop.running());
ASSERT_TRUE(provider->prefetch_done());
// Wait for the provider to finish the non-prefetch request asynchronously.
listener_run_loop.Run();
ASSERT_TRUE(provider->done());
ASSERT_TRUE(controller_->done());
// The results are expected to be non-empty as the provider did notify the
// controller of the non-prefetch request results.
EXPECT_EQ(kResultsPerProvider, result_.size());
}
TEST_F(AutocompleteProviderPrefetchTest, UnsupportedProvider_Prefetch) {
// Add a test provider that does not support prefetch requests.
TestProvider* provider = new TestProvider(kResultsPerProvider, u"http://a",
kTestTemplateURLKeyword, client_);
controller_->providers_.push_back(provider);
base::RunLoop provider_run_loop;
provider->set_closure(provider_run_loop.QuitClosure());
AutocompleteInput input(u"", metrics::OmniboxEventProto::OTHER,
TestingSchemeClassifier());
controller_->StartPrefetch(input);
// We expect this not to call StartPrefetch() on the provider.
ASSERT_FALSE(provider_run_loop.running());
ASSERT_TRUE(provider->prefetch_done());
ASSERT_TRUE(provider->done());
ASSERT_TRUE(controller_->done());
}