[go: nahoru, domu]

blob: 82bb4114042e6a3af5ea0795e165eb1d0946fab2 [file] [log] [blame]
// Copyright 2021 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 "chrome/browser/ui/app_list/search/search_controller_impl_new.h"
#include <memory>
#include <vector>
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "base/task/post_task.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "chrome/browser/ui/app_list/search/ranking/ranker_delegate.h"
#include "chrome/browser/ui/app_list/search/search_controller.h"
#include "chrome/browser/ui/app_list/search/search_provider.h"
#include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
#include "chrome/test/base/chrome_ash_test_base.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace app_list {
namespace {
// TODO(crbug.com/1258415): Since we have a lot of class fakes now, we should
// generalize them and split them into a test utils directory.
using testing::ElementsAreArray;
using testing::UnorderedElementsAreArray;
using Category = ash::AppListSearchResultCategory;
using Result = ash::AppListSearchResultType;
class TestSearchResult : public ChromeSearchResult {
public:
TestSearchResult(const std::string& id,
Category category,
int best_match_rank,
double relevance) {
set_id(id);
SetCategory(category);
scoring().best_match_rank = best_match_rank;
set_relevance(relevance);
scoring().ftrl_result_score = relevance;
}
TestSearchResult(const TestSearchResult&) = delete;
TestSearchResult& operator=(const TestSearchResult&) = delete;
~TestSearchResult() override = default;
private:
void Open(int event_flags) override { NOTIMPLEMENTED(); }
};
class TestSearchProvider : public SearchProvider {
public:
TestSearchProvider(ash::AppListSearchResultType result_type,
bool block_zero_state,
base::TimeDelta delay)
: result_type_(result_type),
block_zero_state_(block_zero_state),
delay_(delay) {}
~TestSearchProvider() override = default;
void SetNextResults(
std::vector<std::unique_ptr<ChromeSearchResult>> results) {
results_ = std::move(results);
}
bool ShouldBlockZeroState() const override { return block_zero_state_; }
ash::AppListSearchResultType ResultType() const override {
return result_type_;
}
void Start(const std::u16string& query) override {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TestSearchProvider::SetResults, base::Unretained(this)),
delay_);
}
void StartZeroState() override {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TestSearchProvider::SetResults, base::Unretained(this)),
delay_);
}
private:
void SetResults() { SwapResults(&results_); }
std::vector<std::unique_ptr<ChromeSearchResult>> results_;
ash::AppListSearchResultType result_type_;
bool block_zero_state_;
base::TimeDelta delay_;
};
// A test ranker delegate that circumvents all result rankings, and hardcodes
// category ranking.
class TestRankerDelegate : public RankerDelegate {
public:
explicit TestRankerDelegate(Profile* profile)
: RankerDelegate(profile, nullptr) {}
~TestRankerDelegate() override {}
TestRankerDelegate(const TestRankerDelegate&) = delete;
TestRankerDelegate& operator=(const TestRankerDelegate&) = delete;
void SetCategoryRanks(base::flat_map<Category, double> category_ranks) {
category_ranks_ = category_ranks;
}
// Ranker:
void UpdateResultRanks(ResultsMap& results, ProviderType provider) override {
// Noop.
}
// Ranker:
void UpdateCategoryRanks(const ResultsMap& results,
CategoriesList& categories,
ProviderType provider) override {
for (auto& category : categories) {
const auto it = category_ranks_.find(category.category);
if (it != category_ranks_.end())
category.score = it->second;
}
}
// Ranker:
void Start(const std::u16string& query,
ResultsMap& results,
CategoriesList& categories) override {}
void Train(const LaunchData& launch) override {}
void Remove(ChromeSearchResult* result) override {}
private:
base::flat_map<Category, double> category_ranks_;
};
std::vector<std::unique_ptr<ChromeSearchResult>> MakeResults(
std::vector<std::string> ids,
std::vector<Category> categories,
std::vector<int> best_match_ranks,
std::vector<double> scores) {
std::vector<std::unique_ptr<ChromeSearchResult>> results;
for (size_t i = 0; i < ids.size(); ++i) {
results.emplace_back(std::make_unique<TestSearchResult>(
ids[i], categories[i], best_match_ranks[i], scores[i]));
}
return results;
}
// Returns a pointer to a search provider. Only valid until the next call to
// SimpleProvider.
static std::unique_ptr<SearchProvider> kProvider;
SearchProvider* SimpleProvider(ash::AppListSearchResultType result_type) {
kProvider = std::make_unique<TestSearchProvider>(result_type, false,
base::Seconds(0));
return kProvider.get();
}
} // namespace
class SearchControllerImplNewTest : public testing::Test {
public:
SearchControllerImplNewTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
SearchControllerImplNewTest(const SearchControllerImplNewTest&) = delete;
SearchControllerImplNewTest& operator=(const SearchControllerImplNewTest&) =
delete;
~SearchControllerImplNewTest() override = default;
void SetUp() override {
// TODO(crbug.com/1258415): Feature list can be removed after launch.
scoped_feature_list_.InitWithFeatures(
{app_list_features::kCategoricalSearch}, {});
search_controller_ = std::make_unique<SearchControllerImplNew>(
/*model_updater=*/&model_updater_, /*list_controller=*/nullptr,
/*notifier=*/nullptr, &profile_);
auto ranker_delegate = std::make_unique<TestRankerDelegate>(&profile_);
ranker_delegate_ = ranker_delegate.get();
search_controller_->set_ranker_delegate_for_test(
std::move(ranker_delegate));
}
void ExpectIdOrder(std::vector<std::string> expected_ids) {
const auto& actual_results = model_updater_.search_results();
EXPECT_EQ(actual_results.size(), expected_ids.size());
std::vector<std::string> actual_ids;
std::transform(actual_results.begin(), actual_results.end(),
std::back_inserter(actual_ids),
[](const ChromeSearchResult* res) -> const std::string& {
return res->id();
});
EXPECT_THAT(actual_ids, ElementsAreArray(expected_ids));
}
// Compares expected category burn-in iteration numbers to those recorded
// within the search controller. The expected list should not include
// categories for which burn-in number is unset (i.e. = -1).
void ExpectCategoriesToBurnInIterations(
std::vector<std::pair<Category, int>>
expected_categories_to_burnin_iteration) {
const auto& actual_categories_list = search_controller_->categories_;
std::vector<std::pair<Category, int>> actual_categories_to_burnin_iteration;
for (const auto& category : actual_categories_list) {
if (category.burnin_iteration != -1) {
actual_categories_to_burnin_iteration.push_back(
{category.category, category.burnin_iteration});
}
}
EXPECT_THAT(
actual_categories_to_burnin_iteration,
UnorderedElementsAreArray(expected_categories_to_burnin_iteration));
}
void ExpectIdsToBurnInIterations(std::vector<std::pair<std::string, int>>
expected_ids_to_burnin_iteration) {
const auto& actual_ids_to_burnin_iteration =
std::vector<std::pair<std::string, int>>(
search_controller_->ids_to_burnin_iteration_.begin(),
search_controller_->ids_to_burnin_iteration_.end());
ASSERT_EQ(actual_ids_to_burnin_iteration.size(),
expected_ids_to_burnin_iteration.size());
EXPECT_THAT(actual_ids_to_burnin_iteration,
UnorderedElementsAreArray(expected_ids_to_burnin_iteration));
}
void Wait() { task_environment_.RunUntilIdle(); }
void ElapseBurnInPeriod() {
task_environment_.FastForwardBy(base::Seconds(1));
}
protected:
content::BrowserTaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
TestingProfile profile_;
FakeAppListModelUpdater model_updater_{&profile_, /*order_delegate=*/nullptr};
std::unique_ptr<SearchControllerImplNew> search_controller_;
// Owned by |search_controller_|.
TestRankerDelegate* ranker_delegate_{nullptr};
};
// Tests that best matches are ordered first, and categories are ignored when
// ranking within best match.
TEST_F(SearchControllerImplNewTest, BestMatchesOrderedAboveOtherResults) {
auto results_1 = MakeResults(
{"a", "b", "c", "d"},
{Category::kWeb, Category::kWeb, Category::kApps, Category::kWeb},
{0, -1, 1, -1}, {0.4, 0.7, 0.2, 0.8});
ranker_delegate_->SetCategoryRanks(
{{Category::kApps, 0.4}, {Category::kWeb, 0.2}});
search_controller_->StartSearch(u"abc");
// Simulate a provider returning and containing the first set of results. A
// single provider wouldn't return many results like this, but that's
// unimportant for the test.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(results_1));
ElapseBurnInPeriod();
// Expect that:
// - best matches are ordered first,
// - best matches are ordered by best match rank,
// - categories are ignored within best match.
ExpectIdOrder({"a", "c", "d", "b"});
// Simulate the arrival of another result into the best match category. Its
// best match rank takes precedence over its relevance score in determining
// its rank within the best matches.
auto results_2 = MakeResults({"e"}, {Category::kFiles}, {2}, {0.9});
search_controller_->SetResults(SimpleProvider(Result::kFileSearch),
std::move(results_2));
ExpectIdOrder({"a", "c", "e", "d", "b"});
}
TEST_F(SearchControllerImplNewTest,
BurnInIterationNumbersTrackedInQuerySearch_Results) {
// This test focuses on the book-keeping of burn-in iteration numbers for
// individual results, and ignores the effect that these numbers can have on
// final sorting of the categories or results lists.
ranker_delegate_->SetCategoryRanks({{Category::kFiles, 0.1}});
// Set up some results from two different providers.
auto file_results = MakeResults({"a"}, {Category::kFiles}, {-1}, {0.9});
auto app_results = MakeResults({"b"}, {Category::kApps}, {-1}, {0.1});
// Set up results from a third different provider. This provider will first
// return one set of results, then later return an updated set of results.
auto web_results_first_arrival = MakeResults(
{"c", "d"}, {Category::kWeb, Category::kWeb}, {-1, -1}, {0.2, 0.1});
auto web_results_second_arrival = MakeResults(
{"c", "d", "e"}, {Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1}, {0.2, 0.1, 0.4});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate providers returning results within the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kFileSearch),
std::move(file_results));
ExpectIdsToBurnInIterations({{"a", 0}});
search_controller_->SetResults(SimpleProvider(Result::kInstalledApp),
std::move(app_results));
ExpectIdsToBurnInIterations({{"a", 0}, {"b", 0}});
// Simulate a provider returning results after the burn-in period.
ElapseBurnInPeriod();
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_first_arrival));
ExpectIdsToBurnInIterations({{"a", 0}, {"b", 0}, {"c", 1}, {"d", 1}});
// Simulate a provider returning for a second time. The burn-in iteration
// number for previously seen results is preserved, while that of newly seen
// results is incremented.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_second_arrival));
ExpectIdsToBurnInIterations(
{{"a", 0}, {"b", 0}, {"c", 1}, {"d", 1}, {"e", 2}});
}
TEST_F(SearchControllerImplNewTest,
BurnInIterationNumbersTrackedInQuerySearch_Categories) {
// This test focuses on the book-keeping of burn-in iteration numbers for
// categories, and ignores the effect that these numbers can have on final
// sorting of the categories or results lists.
ranker_delegate_->SetCategoryRanks({{Category::kFiles, 0.1}});
// Set up some results from four different providers. Only their categories
// are relevant, and individual result scores are not.
auto file_results = MakeResults({"a"}, {Category::kFiles}, {-1}, {0.9});
auto app_results = MakeResults({"b"}, {Category::kApps}, {-1}, {0.1});
// This provider will first return one set of results, then later return an
// updated set of results.
auto web_results_first_arrival = MakeResults(
{"c", "d"}, {Category::kWeb, Category::kWeb}, {-1, -1}, {0.2, 0.1});
auto web_results_second_arrival = MakeResults(
{"c", "d", "e"}, {Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1}, {0.2, 0.1, 0.4});
auto settings_results =
MakeResults({"f"}, {Category::kSettings}, {-1}, {0.8});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate providers returning results within the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kFileSearch),
std::move(file_results));
ExpectCategoriesToBurnInIterations({{Category::kFiles, 0}});
search_controller_->SetResults(SimpleProvider(Result::kInstalledApp),
std::move(app_results));
ExpectCategoriesToBurnInIterations(
{{Category::kFiles, 0}, {Category::kApps, 0}});
// Simulate a third provider returning results after the burn-in period.
ElapseBurnInPeriod();
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_first_arrival));
ExpectCategoriesToBurnInIterations(
{{Category::kFiles, 0}, {Category::kApps, 0}, {Category::kWeb, 1}});
// Simulate the third provider returning for a second time. The burn-in
// iteration number for that category is not updated.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_second_arrival));
ExpectCategoriesToBurnInIterations(
{{Category::kFiles, 0}, {Category::kApps, 0}, {Category::kWeb, 1}});
// Simulate a fourth provider returning for the first time.
search_controller_->SetResults(SimpleProvider(Result::kOsSettings),
std::move(settings_results));
ExpectCategoriesToBurnInIterations({{Category::kFiles, 0},
{Category::kApps, 0},
{Category::kWeb, 1},
{Category::kSettings, 3}});
}
// Tests that categories which arrive pre-burn-in are ordered correctly, and
// their results are grouped together and ordered by score.
TEST_F(SearchControllerImplNewTest, CategoriesOrderedCorrectly_PreBurnIn) {
ranker_delegate_->SetCategoryRanks(
{{Category::kFiles, 0.3}, {Category::kWeb, 0.2}, {Category::kApps, 0.1}});
auto file_results = MakeResults({"a"}, {Category::kFiles}, {-1}, {0.9});
auto web_results = MakeResults(
{"c", "d", "b"}, {Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1}, {0.2, 0.1, 0.4});
auto app_results = MakeResults({"e"}, {Category::kApps}, {-1}, {0.1});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate several providers returning results pre-burn-in.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results));
search_controller_->SetResults(SimpleProvider(Result::kInstalledApp),
std::move(app_results));
search_controller_->SetResults(SimpleProvider(Result::kFileSearch),
std::move(file_results));
ElapseBurnInPeriod();
ExpectIdOrder({"a", "b", "c", "d", "e"});
}
// Tests that categories which arrive post-burn-in are ordered correctly, and
// their results are grouped together and ordered by score.
TEST_F(SearchControllerImplNewTest, CategoriesOrderedCorrectly_PostBurnIn) {
ranker_delegate_->SetCategoryRanks(
{{Category::kFiles, 0.3}, {Category::kWeb, 0.2}, {Category::kApps, 0.1}});
auto web_results = MakeResults(
{"b", "c", "a"}, {Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1}, {0.2, 0.1, 0.4});
auto app_results = MakeResults({"e", "d"}, {Category::kApps, Category::kApps},
{-1, -1}, {0.7, 0.9});
auto file_results = MakeResults({"f"}, {Category::kFiles}, {-1}, {0.8});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate several providers returning results post-burn-in.
ElapseBurnInPeriod();
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results));
ExpectIdOrder({"a", "b", "c"});
search_controller_->SetResults(SimpleProvider(Result::kInstalledApp),
std::move(app_results));
ExpectIdOrder({"a", "b", "c", "d", "e"});
search_controller_->SetResults(SimpleProvider(Result::kFileSearch),
std::move(file_results));
ExpectIdOrder({"a", "b", "c", "d", "e", "f"});
}
// Tests that categories are ordered correctly, where some categories arrive
// pre-burn-in and others arrive post-burn-in. Test that their results are
// grouped together and ordered by score.
TEST_F(
SearchControllerImplNewTest,
CategoriesOrderedCorrectly_PreAndPostBurnIn_OneProviderReturnPerCategory) {
ranker_delegate_->SetCategoryRanks(
{{Category::kFiles, 0.3}, {Category::kWeb, 0.2}, {Category::kApps, 0.1}});
auto web_results = MakeResults(
{"c", "d", "b"}, {Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1}, {0.3, 0.2, 0.4});
auto app_results = MakeResults({"e"}, {Category::kApps}, {-1}, {0.1});
auto file_results = MakeResults({"a"}, {Category::kFiles}, {-1}, {0.9});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate a provider returning results within the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results));
ExpectIdOrder({});
// Expect results to appear after burn-in period has elapsed.
ElapseBurnInPeriod();
ExpectIdOrder({"b", "c", "d"});
// Simulate several providers returning results after the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kInstalledApp),
std::move(app_results));
ExpectIdOrder({"b", "c", "d", "e"});
search_controller_->SetResults(SimpleProvider(Result::kFileSearch),
std::move(file_results));
ExpectIdOrder({"b", "c", "d", "e", "a"});
}
// Tests that results are ordered correctly, where results are of a single
// category, and originate from a single provider which returns multiple times
// both pre- and post-burn-in.
TEST_F(
SearchControllerImplNewTest,
ResultsOrderedCorrectly_PreAndPostBurnIn_SingleProviderReturnsMultipleTimes) {
ranker_delegate_->SetCategoryRanks({{Category::kWeb, 0.2}});
auto web_results_1 = MakeResults(
{"b", "c", "a"}, {Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1}, {0.2, 0.1, 0.3});
auto web_results_2 = MakeResults(
{"b", "c", "a", "d"},
{Category::kWeb, Category::kWeb, Category::kWeb, Category::kWeb},
{-1, -1, -1, -1}, {0.2, 0.1, 0.3, 0.4});
auto web_results_3 =
MakeResults({"b", "c", "a", "d", "e"},
{Category::kWeb, Category::kWeb, Category::kWeb,
Category::kWeb, Category::kWeb},
{-1, -1, -1, -1, -1}, {0.2, 0.1, 0.3, 0.4, 0.5});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate the provider returning results within the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_1));
ExpectIdOrder({});
// Expect results to appear after burn-in period has elapsed.
ElapseBurnInPeriod();
ExpectIdOrder({"a", "b", "c"});
// When a single provider returns multiple times for a category, sorting by
// result burn-in iteration number takes precedence over sorting by result
// score.
//
// Simulate the provider returning results twice after the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_2));
ExpectIdOrder({"a", "b", "c", "d"});
search_controller_->SetResults(SimpleProvider(Result::kOmnibox),
std::move(web_results_3));
ExpectIdOrder({"a", "b", "c", "d", "e"});
}
// Tests that results are ordered correctly, where results are of a single
// category, and originate from multiple providers. Providers return a single
// time, pre- or post-burn-in.
TEST_F(
SearchControllerImplNewTest,
ResultsOrderedCorrectly_PreAndPostBurnIn_MultipleProvidersReturnToSingleCategory) {
ranker_delegate_->SetCategoryRanks({{Category::kWeb, 0.2}});
auto installed_app_results = MakeResults(
{"b", "c", "a"}, {Category::kApps, Category::kApps, Category::kApps},
{-1, -1, -1}, {0.3, 0.2, 0.4});
auto play_store_app_results = MakeResults(
{"e", "d"}, {Category::kApps, Category::kApps}, {-1, -1}, {0.1, 0.5});
auto internal_app_results =
MakeResults({"f"}, {Category::kApps}, {-1}, {0.9});
// Simulate starting a search.
search_controller_->StartSearch(u"abc");
// Simulate a provider returning results within the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kInstalledApp),
std::move(installed_app_results));
ExpectIdOrder({});
// Expect results to appear after burn-in period has elapsed.
ElapseBurnInPeriod();
ExpectIdOrder({"a", "b", "c"});
// When there are multiple providers returning for a category, sorting by
// burn-in iteration number takes precedence over sorting by result score.
//
// Simulate two other providers returning results after the burn-in period.
search_controller_->SetResults(SimpleProvider(Result::kPlayStoreApp),
std::move(play_store_app_results));
ExpectIdOrder({"a", "b", "c", "d", "e"});
search_controller_->SetResults(SimpleProvider(Result::kInternalApp),
std::move(internal_app_results));
ExpectIdOrder({"a", "b", "c", "d", "e", "f"});
}
TEST_F(SearchControllerImplNewTest, FirstSearchResultsNotShownInSecondSearch) {
ranker_delegate_->SetCategoryRanks({{Category::kApps, 0.1}});
auto provider = std::make_unique<TestSearchProvider>(Result::kInstalledApp,
false, base::Seconds(1));
auto* provider_ptr = provider.get();
search_controller_->AddProvider(0, std::move(provider));
// Start the first search.
provider_ptr->SetNextResults(
MakeResults({"AAA"}, {Category::kApps}, {-1}, {0.1}));
search_controller_->StartSearch(u"A");
ExpectIdOrder({});
// Provider has returned and the A result should be published.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({"AAA"});
provider_ptr->SetNextResults({});
search_controller_->StartZeroState(base::DoNothing(), base::Seconds(1));
task_environment_.FastForwardBy(base::Seconds(1));
// Start the second search.
provider_ptr->SetNextResults(
MakeResults({"BBB"}, {Category::kApps}, {-1}, {0.1}));
search_controller_->StartSearch(u"B");
// The B result is not ready yet, and the A result should *not* have been
// published.
ExpectIdOrder({});
// Provider has returned and the B result should be published.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({"BBB"});
}
TEST_F(SearchControllerImplNewTest, ZeroStateResultsAreBlocked) {
ranker_delegate_->SetCategoryRanks({{Category::kApps, 0.1}});
// Set up four providers, two are zero-state blocking. One is slow. The
// particular result types and categories don't matter.
auto provider_a = std::make_unique<TestSearchProvider>(
Result::kInstalledApp, true, base::Seconds(1));
auto provider_b = std::make_unique<TestSearchProvider>(
Result::kZeroStateFile, true, base::Seconds(2));
auto provider_c = std::make_unique<TestSearchProvider>(
Result::kOsSettings, false, base::Seconds(1));
auto provider_d = std::make_unique<TestSearchProvider>(
Result::kOmnibox, false, base::Seconds(4));
provider_a->SetNextResults(
MakeResults({"a"}, {Category::kApps}, {-1}, {0.3}));
provider_b->SetNextResults(
MakeResults({"b"}, {Category::kApps}, {-1}, {0.2}));
provider_c->SetNextResults(
MakeResults({"c"}, {Category::kApps}, {-1}, {0.1}));
provider_d->SetNextResults(
MakeResults({"d"}, {Category::kApps}, {-1}, {0.4}));
search_controller_->AddProvider(0, std::move(provider_a));
search_controller_->AddProvider(0, std::move(provider_b));
search_controller_->AddProvider(0, std::move(provider_c));
search_controller_->AddProvider(0, std::move(provider_d));
// Start the zero-state session. When on-done is called, we should have
// results from all but the slowest provider.
search_controller_->StartZeroState(base::BindLambdaForTesting([&]() {
ExpectIdOrder({"a", "b", "c"});
}),
base::Seconds(3));
// The fast provider has returned but shouldn't have published.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({});
// Additionally, those three results should be returned before the
// StartZeroState timeout.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({"a", "b", "c"});
// The latecomer should still be added when it arrives.
task_environment_.FastForwardBy(base::Seconds(2));
ExpectIdOrder({"d", "a", "b", "c"});
}
TEST_F(SearchControllerImplNewTest, ZeroStateResultsGetTimedOut) {
ranker_delegate_->SetCategoryRanks({{Category::kApps, 0.1}});
auto provider_a = std::make_unique<TestSearchProvider>(
Result::kInstalledApp, true, base::Seconds(1));
auto provider_b = std::make_unique<TestSearchProvider>(
Result::kZeroStateFile, true, base::Seconds(3));
provider_a->SetNextResults(
MakeResults({"a"}, {Category::kApps}, {-1}, {0.3}));
provider_b->SetNextResults(
MakeResults({"b"}, {Category::kFiles}, {-1}, {0.2}));
search_controller_->AddProvider(0, std::move(provider_a));
search_controller_->AddProvider(0, std::move(provider_b));
search_controller_->StartZeroState(
base::BindLambdaForTesting([&]() { ExpectIdOrder({"a"}); }),
base::Seconds(2));
// The fast provider has returned but shouldn't have published.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({});
// The timeout finished, the fast provider's result should be published.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({"a"});
// The slow provider should still publish when it returns.
task_environment_.FastForwardBy(base::Seconds(1));
ExpectIdOrder({"a", "b"});
}
TEST_F(SearchControllerImplNewTest, ContinueRanksDriveAboveLocal) {
// Use the full ranking stack.
search_controller_->set_ranker_delegate_for_test(
std::make_unique<RankerDelegate>(&profile_, search_controller_.get()));
auto drive_provider = std::make_unique<TestSearchProvider>(
Result::kZeroStateDrive, true, base::Seconds(0));
auto local_provider = std::make_unique<TestSearchProvider>(
Result::kZeroStateFile, true, base::Seconds(0));
drive_provider->SetNextResults(MakeResults(
{"drive_a", "drive_b"}, {Category::kUnknown, Category::kUnknown},
{-1, -1}, {0.2, 0.1}));
local_provider->SetNextResults(MakeResults(
{"local_a", "local_b"}, {Category::kUnknown, Category::kUnknown},
{-1, -1}, {0.5, 0.4}));
search_controller_->AddProvider(0, std::move(local_provider));
search_controller_->AddProvider(0, std::move(drive_provider));
search_controller_->StartZeroState(base::DoNothing(), base::Seconds(1));
Wait();
ExpectIdOrder({"drive_a", "drive_b", "local_a", "local_b"});
}
} // namespace app_list