Commit e7685508 authored by Tomasz Wiszkowski's avatar Tomasz Wiszkowski Committed by Chromium LUCI CQ

Move voice suggestions provider to native code.

This change enforces coherency between Java and Native
AutocompleteResult in the situation where some of the suggestions
originate from the voice input.

This change solves the following issues:
- GroupSuggestionsBySearchVsURL will include the voice suggestions,
- Suggestions can be merged / deduplicated with existing entries
- Voice Suggestions will not be listed below grouped entries
  (suggestions with header),
- We are now able to properly report these suggestions to GWS,
- We should see fewer reports in Omnibox.Android.InvalidMatch as
  Java entries will now have corresponding Native matches.

Bug: 1151794
Change-Id: I575670781e744f3ac832705c830f3e0e506a83ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2568850
Commit-Queue: Tomasz Wiszkowski <ender@google.com>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarTommy Li <tommycli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834980}
parent f269dbe7
...@@ -45,6 +45,7 @@ public class AutocompleteController { ...@@ -45,6 +45,7 @@ public class AutocompleteController {
private final VoiceSuggestionProvider mVoiceSuggestionProvider = new VoiceSuggestionProvider(); private final VoiceSuggestionProvider mVoiceSuggestionProvider = new VoiceSuggestionProvider();
private boolean mUseCachedZeroSuggestResults; private boolean mUseCachedZeroSuggestResults;
private boolean mEnableNativeVoiceSuggestProvider;
private boolean mWaitingForSuggestionsToCache; private boolean mWaitingForSuggestionsToCache;
/** /**
...@@ -80,6 +81,8 @@ public class AutocompleteController { ...@@ -80,6 +81,8 @@ public class AutocompleteController {
return; return;
} }
mEnableNativeVoiceSuggestProvider = ChromeFeatureList.isEnabled(
ChromeFeatureList.OMNIBOX_NATIVE_VOICE_SUGGEST_PROVIDER);
mNativeAutocompleteControllerAndroid = mNativeAutocompleteControllerAndroid =
AutocompleteControllerJni.get().init(AutocompleteController.this, profile); AutocompleteControllerJni.get().init(AutocompleteController.this, profile);
} }
...@@ -251,19 +254,23 @@ public class AutocompleteController { ...@@ -251,19 +254,23 @@ public class AutocompleteController {
protected void onSuggestionsReceived(AutocompleteResult autocompleteResult, protected void onSuggestionsReceived(AutocompleteResult autocompleteResult,
String inlineAutocompleteText, long currentNativeAutocompleteResult) { String inlineAutocompleteText, long currentNativeAutocompleteResult) {
assert mListener != null : "Ensure a listener is set prior generating suggestions."; assert mListener != null : "Ensure a listener is set prior generating suggestions.";
final AutocompleteResult originalResult = autocompleteResult;
// Run through new providers to get an updated list of suggestions. // Run through new providers to get an updated list of suggestions.
AutocompleteResult resultsWithVoiceSuggestions = new AutocompleteResult( if (!mEnableNativeVoiceSuggestProvider) {
autocompleteResult = new AutocompleteResult(
mVoiceSuggestionProvider.addVoiceSuggestions( mVoiceSuggestionProvider.addVoiceSuggestions(
autocompleteResult.getSuggestionsList(), MAX_VOICE_SUGGESTION_COUNT), autocompleteResult.getSuggestionsList(), MAX_VOICE_SUGGESTION_COUNT),
autocompleteResult.getGroupsDetails()); autocompleteResult.getGroupsDetails());
}
mCurrentNativeAutocompleteResult = currentNativeAutocompleteResult; mCurrentNativeAutocompleteResult = currentNativeAutocompleteResult;
// Notify callbacks of suggestions. // Notify callbacks of suggestions.
mListener.onSuggestionsReceived(resultsWithVoiceSuggestions, inlineAutocompleteText); mListener.onSuggestionsReceived(autocompleteResult, inlineAutocompleteText);
if (mWaitingForSuggestionsToCache) { if (mWaitingForSuggestionsToCache) {
CachedZeroSuggestionsManager.saveToCache(autocompleteResult); CachedZeroSuggestionsManager.saveToCache(originalResult);
} }
} }
...@@ -303,7 +310,20 @@ public class AutocompleteController { ...@@ -303,7 +310,20 @@ public class AutocompleteController {
* @param results A list containing the results of a voice recognition. * @param results A list containing the results of a voice recognition.
*/ */
void onVoiceResults(@Nullable List<VoiceResult> results) { void onVoiceResults(@Nullable List<VoiceResult> results) {
if (!mEnableNativeVoiceSuggestProvider) {
mVoiceSuggestionProvider.setVoiceResults(results); mVoiceSuggestionProvider.setVoiceResults(results);
} else {
if (results == null || results.size() == 0) return;
final int count = Math.min(results.size(), MAX_VOICE_SUGGESTION_COUNT);
String[] voiceMatches = new String[count];
float[] confidenceScores = new float[count];
for (int i = 0; i < count; i++) {
voiceMatches[i] = results.get(i).getMatch();
confidenceScores[i] = results.get(i).getConfidence();
}
AutocompleteControllerJni.get().setVoiceMatches(
mNativeAutocompleteControllerAndroid, voiceMatches, confidenceScores);
}
} }
/** /**
...@@ -421,6 +441,8 @@ public class AutocompleteController { ...@@ -421,6 +441,8 @@ public class AutocompleteController {
long nativeAutocompleteControllerAndroid, AutocompleteController caller, GURL url); long nativeAutocompleteControllerAndroid, AutocompleteController caller, GURL url);
void groupSuggestionsBySearchVsURL( void groupSuggestionsBySearchVsURL(
long nativeAutocompleteControllerAndroid, int firstIndex, int lastIndex); long nativeAutocompleteControllerAndroid, int firstIndex, int lastIndex);
void setVoiceMatches(long nativeAutocompleteControllerAndroid, String[] matches,
float[] confidenceScores);
/** /**
* Given a search query, this will attempt to see if the query appears to be portion of a * Given a search query, this will attempt to see if the query appears to be portion of a
* properly formed URL. If it appears to be a URL, this will return the fully qualified * properly formed URL. If it appears to be a URL, this will return the fully qualified
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include "components/omnibox/browser/omnibox_log.h" #include "components/omnibox/browser/omnibox_log.h"
#include "components/omnibox/browser/search_provider.h" #include "components/omnibox/browser/search_provider.h"
#include "components/omnibox/browser/search_suggestion_parser.h" #include "components/omnibox/browser/search_suggestion_parser.h"
#include "components/omnibox/browser/voice_suggest_provider.h"
#include "components/omnibox/browser/zero_suggest_provider.h" #include "components/omnibox/browser/zero_suggest_provider.h"
#include "components/omnibox/common/omnibox_features.h" #include "components/omnibox/common/omnibox_features.h"
#include "components/open_from_clipboard/clipboard_recent_content.h" #include "components/open_from_clipboard/clipboard_recent_content.h"
...@@ -531,6 +532,29 @@ void AutocompleteControllerAndroid::NotifySuggestionsReceived( ...@@ -531,6 +532,29 @@ void AutocompleteControllerAndroid::NotifySuggestionsReceived(
inline_text, j_autocomplete_result_raw_ptr); inline_text, j_autocomplete_result_raw_ptr);
} }
void AutocompleteControllerAndroid::SetVoiceMatches(
JNIEnv* env,
const JavaParamRef<jobjectArray>& j_voice_matches,
const JavaParamRef<jfloatArray>& j_confidence_scores) {
auto* const voice_suggest_provider =
autocomplete_controller_->voice_suggest_provider();
DCHECK(voice_suggest_provider)
<< "Voice matches received with no registered VoiceSuggestProvider. "
<< "Either disable voice input, or provision VoiceSuggestProvider.";
std::vector<base::string16> voice_matches;
std::vector<float> confidence_scores;
AppendJavaStringArrayToStringVector(env, j_voice_matches, &voice_matches);
JavaFloatArrayToFloatVector(env, j_confidence_scores, &confidence_scores);
DCHECK(voice_matches.size() == confidence_scores.size());
voice_suggest_provider->ClearCache();
for (size_t index = 0; index < voice_matches.size(); ++index) {
voice_suggest_provider->AddVoiceSuggestion(voice_matches[index],
confidence_scores[index]);
}
}
bool AutocompleteControllerAndroid::IsValidMatch(JNIEnv* env, bool AutocompleteControllerAndroid::IsValidMatch(JNIEnv* env,
jint selected_index, jint selected_index,
jint hash_code) { jint hash_code) {
......
...@@ -128,6 +128,12 @@ class AutocompleteControllerAndroid : public AutocompleteController::Observer, ...@@ -128,6 +128,12 @@ class AutocompleteControllerAndroid : public AutocompleteController::Observer,
content::BrowserContext* profile) const override; content::BrowserContext* profile) const override;
}; };
// Pass detected voice matches down to VoiceSuggestionsProvider.
void SetVoiceMatches(
JNIEnv* env,
const base::android::JavaParamRef<jobjectArray>& j_voice_matches,
const base::android::JavaParamRef<jfloatArray>& j_confidence_scores);
private: private:
~AutocompleteControllerAndroid() override; ~AutocompleteControllerAndroid() override;
void InitJNI(JNIEnv* env, jobject obj); void InitJNI(JNIEnv* env, jobject obj);
......
...@@ -209,6 +209,8 @@ static_library("browser") { ...@@ -209,6 +209,8 @@ static_library("browser") {
"url_prefix.h", "url_prefix.h",
"verbatim_match.cc", "verbatim_match.cc",
"verbatim_match.h", "verbatim_match.h",
"voice_suggest_provider.cc",
"voice_suggest_provider.h",
"zero_suggest_provider.cc", "zero_suggest_provider.cc",
"zero_suggest_provider.h", "zero_suggest_provider.h",
"zero_suggest_verbatim_match_provider.cc", "zero_suggest_verbatim_match_provider.cc",
...@@ -537,6 +539,7 @@ source_set("unit_tests") { ...@@ -537,6 +539,7 @@ source_set("unit_tests") {
"tailored_word_break_iterator_unittest.cc", "tailored_word_break_iterator_unittest.cc",
"titled_url_match_utils_unittest.cc", "titled_url_match_utils_unittest.cc",
"url_prefix_unittest.cc", "url_prefix_unittest.cc",
"voice_suggest_provider_unittest.cc",
"zero_suggest_provider_unittest.cc", "zero_suggest_provider_unittest.cc",
"zero_suggest_verbatim_match_provider_unittest.cc", "zero_suggest_verbatim_match_provider_unittest.cc",
] ]
......
...@@ -47,6 +47,9 @@ int AutocompleteClassifier::DefaultOmniboxProviders() { ...@@ -47,6 +47,9 @@ int AutocompleteClassifier::DefaultOmniboxProviders() {
AutocompleteProvider::TYPE_CLIPBOARD | AutocompleteProvider::TYPE_CLIPBOARD |
AutocompleteProvider::TYPE_MOST_VISITED_SITES | AutocompleteProvider::TYPE_MOST_VISITED_SITES |
AutocompleteProvider::TYPE_VERBATIM_MATCH | AutocompleteProvider::TYPE_VERBATIM_MATCH |
#endif
#if defined(OS_ANDROID)
AutocompleteProvider::TYPE_VOICE_SUGGEST |
#endif #endif
AutocompleteProvider::TYPE_ZERO_SUGGEST | AutocompleteProvider::TYPE_ZERO_SUGGEST |
AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY | AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY |
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "components/omnibox/browser/query_tile_provider.h" #include "components/omnibox/browser/query_tile_provider.h"
#include "components/omnibox/browser/search_provider.h" #include "components/omnibox/browser/search_provider.h"
#include "components/omnibox/browser/shortcuts_provider.h" #include "components/omnibox/browser/shortcuts_provider.h"
#include "components/omnibox/browser/voice_suggest_provider.h"
#include "components/omnibox/browser/zero_suggest_provider.h" #include "components/omnibox/browser/zero_suggest_provider.h"
#include "components/omnibox/browser/zero_suggest_verbatim_match_provider.h" #include "components/omnibox/browser/zero_suggest_verbatim_match_provider.h"
#include "components/omnibox/common/omnibox_features.h" #include "components/omnibox/common/omnibox_features.h"
...@@ -345,6 +346,12 @@ AutocompleteController::AutocompleteController( ...@@ -345,6 +346,12 @@ AutocompleteController::AutocompleteController(
if (provider_types & AutocompleteProvider::TYPE_QUERY_TILE) if (provider_types & AutocompleteProvider::TYPE_QUERY_TILE)
providers_.push_back(new QueryTileProvider(provider_client_.get(), this)); providers_.push_back(new QueryTileProvider(provider_client_.get(), this));
if (provider_types & AutocompleteProvider::TYPE_VOICE_SUGGEST) {
voice_suggest_provider_ =
new VoiceSuggestProvider(provider_client_.get(), this);
providers_.push_back(voice_suggest_provider_);
}
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "AutocompleteController", base::ThreadTaskRunnerHandle::Get()); this, "AutocompleteController", base::ThreadTaskRunnerHandle::Get());
} }
......
...@@ -31,6 +31,7 @@ class HistoryURLProvider; ...@@ -31,6 +31,7 @@ class HistoryURLProvider;
class KeywordProvider; class KeywordProvider;
class SearchProvider; class SearchProvider;
class TemplateURLService; class TemplateURLService;
class VoiceSuggestProvider;
class ZeroSuggestProvider; class ZeroSuggestProvider;
class OnDeviceHeadProvider; class OnDeviceHeadProvider;
...@@ -161,6 +162,9 @@ class AutocompleteController : public AutocompleteProviderListener, ...@@ -161,6 +162,9 @@ class AutocompleteController : public AutocompleteProviderListener,
KeywordProvider* keyword_provider() const { return keyword_provider_; } KeywordProvider* keyword_provider() const { return keyword_provider_; }
SearchProvider* search_provider() const { return search_provider_; } SearchProvider* search_provider() const { return search_provider_; }
ClipboardProvider* clipboard_provider() const { return clipboard_provider_; } ClipboardProvider* clipboard_provider() const { return clipboard_provider_; }
VoiceSuggestProvider* voice_suggest_provider() const {
return voice_suggest_provider_;
}
const AutocompleteInput& input() const { return input_; } const AutocompleteInput& input() const { return input_; }
const AutocompleteResult& result() const { return result_; } const AutocompleteResult& result() const { return result_; }
...@@ -302,6 +306,8 @@ class AutocompleteController : public AutocompleteProviderListener, ...@@ -302,6 +306,8 @@ class AutocompleteController : public AutocompleteProviderListener,
ClipboardProvider* clipboard_provider_; ClipboardProvider* clipboard_provider_;
VoiceSuggestProvider* voice_suggest_provider_;
// Input passed to Start. // Input passed to Start.
AutocompleteInput input_; AutocompleteInput input_;
......
...@@ -66,6 +66,8 @@ const char* AutocompleteProvider::TypeToString(Type type) { ...@@ -66,6 +66,8 @@ const char* AutocompleteProvider::TypeToString(Type type) {
return "MostVisitedSites"; return "MostVisitedSites";
case TYPE_VERBATIM_MATCH: case TYPE_VERBATIM_MATCH:
return "VerbatimMatch"; return "VerbatimMatch";
case TYPE_VOICE_SUGGEST:
return "VoiceSuggest";
default: default:
NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type; NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type;
return "Unknown"; return "Unknown";
...@@ -142,6 +144,8 @@ metrics::OmniboxEventProto_ProviderType AutocompleteProvider:: ...@@ -142,6 +144,8 @@ metrics::OmniboxEventProto_ProviderType AutocompleteProvider::
return metrics::OmniboxEventProto::ZERO_SUGGEST; return metrics::OmniboxEventProto::ZERO_SUGGEST;
case TYPE_VERBATIM_MATCH: case TYPE_VERBATIM_MATCH:
return metrics::OmniboxEventProto::ZERO_SUGGEST; return metrics::OmniboxEventProto::ZERO_SUGGEST;
case TYPE_VOICE_SUGGEST:
return metrics::OmniboxEventProto::SEARCH;
default: default:
NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type_; NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type_;
return metrics::OmniboxEventProto::UNKNOWN_PROVIDER; return metrics::OmniboxEventProto::UNKNOWN_PROVIDER;
......
...@@ -153,6 +153,7 @@ class AutocompleteProvider ...@@ -153,6 +153,7 @@ class AutocompleteProvider
TYPE_QUERY_TILE = 1 << 12, TYPE_QUERY_TILE = 1 << 12,
TYPE_MOST_VISITED_SITES = 1 << 13, TYPE_MOST_VISITED_SITES = 1 << 13,
TYPE_VERBATIM_MATCH = 1 << 14, TYPE_VERBATIM_MATCH = 1 << 14,
TYPE_VOICE_SUGGEST = 1 << 15,
}; };
explicit AutocompleteProvider(Type type); explicit AutocompleteProvider(Type type);
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/voice_suggest_provider.h"
#include <string>
#include "base/strings/string16.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_match_classification.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
namespace {
// Maximum and minimum score allowed for voice suggestions.
// The score is multiplied by confidence score to produce suggestion relevance.
constexpr const int kMaxVoiceSuggestionScore = 1250;
constexpr const int kMinVoiceSuggestionScore = 350;
constexpr const float kConfidenceAlternativesCutoff = 0.8f;
constexpr const float kConfidenceRelevanceCutoff = 0.3f;
constexpr const int kMaxVoiceMatchesToOffer = 3;
// Calculate relevance score for voice suggestion from confidence score.
constexpr int ConfidenceScoreToSuggestionScore(float confidence_score) {
return (kMaxVoiceSuggestionScore - kMinVoiceSuggestionScore) *
confidence_score +
kMinVoiceSuggestionScore;
}
} // namespace
VoiceSuggestProvider::VoiceSuggestProvider(
AutocompleteProviderClient* client,
AutocompleteProviderListener* listener)
: BaseSearchProvider(TYPE_VOICE_SUGGEST, client) {}
VoiceSuggestProvider::~VoiceSuggestProvider() = default;
void VoiceSuggestProvider::Start(const AutocompleteInput& input,
bool minimal_changes) {
autocomplete_input_ = &input;
MatchMap map;
int index = 0;
for (const auto& score_and_suggestion_pair : voice_matches_) {
// Drop suggestions that do not meet the bar.
if (score_and_suggestion_pair.first < kConfidenceRelevanceCutoff)
break;
AddMatchToMap(
SearchSuggestionParser::SuggestResult(
score_and_suggestion_pair.second,
AutocompleteMatchType::VOICE_SUGGEST, {}, false,
ConfidenceScoreToSuggestionScore(score_and_suggestion_pair.first),
false, {}),
{}, index, false, false, &map);
++index;
// Stop if the first voice suggestion has a high relevance score suggesting
// it is properly identified, or if we supplied enough voice matches.
if ((score_and_suggestion_pair.first >= kConfidenceAlternativesCutoff) ||
(index >= kMaxVoiceMatchesToOffer))
break;
}
for (auto& match_pair : map) {
matches_.push_back(std::move(match_pair.second));
}
autocomplete_input_ = nullptr;
}
const TemplateURL* VoiceSuggestProvider::GetTemplateURL(bool is_keyword) const {
DCHECK(!is_keyword);
return client()->GetTemplateURLService()->GetDefaultSearchProvider();
}
const AutocompleteInput VoiceSuggestProvider::GetInput(bool is_keyword) const {
DCHECK(!is_keyword);
DCHECK(autocomplete_input_);
return *autocomplete_input_;
}
bool VoiceSuggestProvider::ShouldAppendExtraParams(
const SearchSuggestionParser::SuggestResult& result) const {
// We always use the default provider for search, so append the params.
return true;
}
void VoiceSuggestProvider::RecordDeletionResult(bool success) {}
void VoiceSuggestProvider::Stop(bool clear_cached_results,
bool due_to_user_inactivity) {
if (clear_cached_results) {
ClearCache();
}
matches_.clear();
}
void VoiceSuggestProvider::ClearCache() {
voice_matches_.clear();
}
void VoiceSuggestProvider::AddVoiceSuggestion(base::string16 voice_match,
float confidence_score) {
voice_matches_.emplace_back(confidence_score, std::move(voice_match));
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_OMNIBOX_BROWSER_VOICE_SUGGEST_PROVIDER_H_
#define COMPONENTS_OMNIBOX_BROWSER_VOICE_SUGGEST_PROVIDER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/base_search_provider.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
// Autocomplete provider serving Voice suggestions on Android.
class VoiceSuggestProvider : public BaseSearchProvider {
public:
VoiceSuggestProvider(AutocompleteProviderClient* client,
AutocompleteProviderListener* listener);
void Start(const AutocompleteInput& input, bool minimal_changes) override;
void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
// Adds voice suggestion to the list of reported AutocompleteMatches.
// The voice suggestion is next converted to a proper Search suggestion
// associated with user-selected search engine, with a relevance score
// computed from the match_score.
void AddVoiceSuggestion(base::string16 match_text, float match_score);
// Clear all cached voice matches.
void ClearCache();
private:
// BaseSearchProvider:
~VoiceSuggestProvider() override;
const TemplateURL* GetTemplateURL(bool is_keyword) const override;
const AutocompleteInput GetInput(bool is_keyword) const override;
bool ShouldAppendExtraParams(
const SearchSuggestionParser::SuggestResult& result) const override;
void RecordDeletionResult(bool success) override;
// A list of voice matches and their confidence scores. The first element
// indicates how confident the voice recognition system is about the accuracy
// of the match, whereas the second element of the pair holds the match text
// itself.
// Multiple matches may hold the same confidence score and/or match text -
// the score will next be used to filter out low-quality matches, and compute
// the relevance score for matches.
// Duplicate voice matches will be deduplicated automatically to the higher
// ranked match.
std::vector<std::pair<float, base::string16>> voice_matches_;
// A pointer to the current AutocompleteInput, retained during the active
// stage of operation only. Used by the BaseSearchProvider to construct the
// final AutocompleteMatch objects.
const AutocompleteInput* autocomplete_input_{};
};
#endif // COMPONENTS_OMNIBOX_BROWSER_VOICE_SUGGEST_PROVIDER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/voice_suggest_provider.h"
#include <memory>
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/fake_autocomplete_provider_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
class VoiceSuggestProviderTest : public testing::Test,
public AutocompleteProviderListener {
public:
VoiceSuggestProviderTest() = default;
VoiceSuggestProviderTest(const VoiceSuggestProviderTest&) = delete;
VoiceSuggestProviderTest& operator=(const VoiceSuggestProviderTest&) = delete;
void SetUp() override;
void TearDown() override;
protected:
// AutocompleteProviderListener:
void OnProviderUpdate(bool updated_matches) override;
base::test::TaskEnvironment environment_;
FakeAutocompleteProviderClient client_;
scoped_refptr<VoiceSuggestProvider> provider_;
std::unique_ptr<AutocompleteInput> input_;
};
void VoiceSuggestProviderTest::SetUp() {
provider_ = base::MakeRefCounted<VoiceSuggestProvider>(&client_, this);
input_ = std::make_unique<AutocompleteInput>(
base::string16(), metrics::OmniboxEventProto::OTHER,
TestSchemeClassifier());
}
void VoiceSuggestProviderTest::TearDown() {
provider_->Stop(true, true);
}
void VoiceSuggestProviderTest::OnProviderUpdate(bool updated_matches) {}
TEST_F(VoiceSuggestProviderTest, ServesNoSuggestionsByDefault) {
provider_->Start(*input_, false);
EXPECT_TRUE(provider_->matches().empty());
}
TEST_F(VoiceSuggestProviderTest, ServesSuppliedVoiceSuggestions) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.6f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.5f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.4f);
provider_->Start(*input_, false);
ASSERT_EQ(3U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
EXPECT_EQ(base::ASCIIToUTF16("Bob"), provider_->matches()[1].contents);
EXPECT_EQ(base::ASCIIToUTF16("Carol"), provider_->matches()[2].contents);
}
TEST_F(VoiceSuggestProviderTest, ConfidenceScoreImpliesOrdering) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.4f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.5f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.6f);
provider_->Start(*input_, false);
ASSERT_EQ(3U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
EXPECT_EQ(base::ASCIIToUTF16("Bob"), provider_->matches()[1].contents);
EXPECT_EQ(base::ASCIIToUTF16("Carol"), provider_->matches()[2].contents);
}
TEST_F(VoiceSuggestProviderTest,
VoiceSuggestionsAreNotReusedInSubsequentRequests) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.6f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.5f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.4f);
provider_->Start(*input_, false);
provider_->Stop(true, false);
provider_->Start(*input_, false);
EXPECT_TRUE(provider_->matches().empty());
}
TEST_F(VoiceSuggestProviderTest, ClearCachePurgesAvailableVoiceSuggestions) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.6f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.5f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.4f);
provider_->ClearCache();
provider_->Start(*input_, false);
EXPECT_TRUE(provider_->matches().empty());
}
TEST_F(VoiceSuggestProviderTest, MatchesWithSameScoresAreNotDropped) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.6f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.6f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.6f);
provider_->Start(*input_, false);
ASSERT_EQ(3U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
EXPECT_EQ(base::ASCIIToUTF16("Bob"), provider_->matches()[1].contents);
EXPECT_EQ(base::ASCIIToUTF16("Carol"), provider_->matches()[2].contents);
}
TEST_F(VoiceSuggestProviderTest, DuplicateMatchesAreMerged) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.6f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.5f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.4f);
provider_->Start(*input_, false);
ASSERT_EQ(2U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
EXPECT_EQ(base::ASCIIToUTF16("Bob"), provider_->matches()[1].contents);
}
TEST_F(VoiceSuggestProviderTest, HighConfidenceScoreDropsAlternatives) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.9f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.5f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.4f);
provider_->Start(*input_, false);
ASSERT_EQ(1U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
}
TEST_F(VoiceSuggestProviderTest, LowConfidenceScoresAreRejected) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.35f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.25f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.2f);
provider_->Start(*input_, false);
ASSERT_EQ(1U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
}
TEST_F(VoiceSuggestProviderTest, VoiceSuggestionResultsCanBeLimited) {
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Alice"), 0.75f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Bob"), 0.65f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Carol"), 0.55f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Dave"), 0.45f);
provider_->AddVoiceSuggestion(base::ASCIIToUTF16("Eve"), 0.35f);
provider_->Start(*input_, false);
ASSERT_EQ(3U, provider_->matches().size());
EXPECT_EQ(base::ASCIIToUTF16("Alice"), provider_->matches()[0].contents);
EXPECT_EQ(base::ASCIIToUTF16("Bob"), provider_->matches()[1].contents);
EXPECT_EQ(base::ASCIIToUTF16("Carol"), provider_->matches()[2].contents);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment