Commit a7752d87 authored by Eleni Dimitriadis's avatar Eleni Dimitriadis Committed by Commit Bot

[Suggested Files] Ranking model

Added a new ChipRanker type, which will house a new Dolphin model.
This model will rank apps alongside both local and drive files, which
will allow us to compare the different types of items.
These ranks will be used to adjust the scores of the files to be
in line with the app scores, while still preserving the original
ordering of both groups.
This ensures the files will display appropriately in the suggestion
chips, but that their ranking elsewhere will be unaffected.

Bug: 1034842
Change-Id: I618e2694b74c6c5917eef66a900ef822f5422b92
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1989742Reviewed-by: default avatarTony Yeoman <tby@chromium.org>
Reviewed-by: default avatarJia Meng <jiameng@chromium.org>
Commit-Queue: Eleni Dimitriadis <edimitriadis@google.com>
Cr-Commit-Position: refs/heads/master@{#743401}
parent e4669d48
...@@ -3805,6 +3805,8 @@ jumbo_static_library("ui") { ...@@ -3805,6 +3805,8 @@ jumbo_static_library("ui") {
"app_list/search/search_result_ranker/app_list_launch_recorder_util.h", "app_list/search/search_result_ranker/app_list_launch_recorder_util.h",
"app_list/search/search_result_ranker/app_search_result_ranker.cc", "app_list/search/search_result_ranker/app_search_result_ranker.cc",
"app_list/search/search_result_ranker/app_search_result_ranker.h", "app_list/search/search_result_ranker/app_search_result_ranker.h",
"app_list/search/search_result_ranker/chip_ranker.cc",
"app_list/search/search_result_ranker/chip_ranker.h",
"app_list/search/search_result_ranker/frecency_store.cc", "app_list/search/search_result_ranker/frecency_store.cc",
"app_list/search/search_result_ranker/frecency_store.h", "app_list/search/search_result_ranker/frecency_store.h",
"app_list/search/search_result_ranker/histogram_util.cc", "app_list/search/search_result_ranker/histogram_util.cc",
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/ui/app_list/search/drive_quick_access_provider.h" #include "chrome/browser/ui/app_list/search/drive_quick_access_provider.h"
#include <algorithm>
#include <memory> #include <memory>
#include <utility> #include <utility>
...@@ -202,6 +203,17 @@ void DriveQuickAccessProvider::OnGetQuickAccessItems( ...@@ -202,6 +203,17 @@ void DriveQuickAccessProvider::OnGetQuickAccessItems(
void DriveQuickAccessProvider::SetResultsCache( void DriveQuickAccessProvider::SetResultsCache(
const std::vector<drive::QuickAccessItem>& drive_results) { const std::vector<drive::QuickAccessItem>& drive_results) {
// Rescale items between 0 and 1
double hi = drive_results[0].confidence;
double lo = drive_results[0].confidence;
for (auto item : drive_results) {
hi = std::max(item.confidence, hi);
lo = std::min(item.confidence, lo);
}
for (auto item : drive_results) {
item.confidence = (item.confidence - lo) / (hi - lo);
}
results_cache_ = std::move(drive_results); results_cache_ = std::move(drive_results);
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "chrome/browser/ui/app_list/app_list_model_updater.h" #include "chrome/browser/ui/app_list/app_list_model_updater.h"
#include "chrome/browser/ui/app_list/search/chrome_search_result.h" #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "chrome/browser/ui/app_list/search/search_provider.h" #include "chrome/browser/ui/app_list/search/search_provider.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h"
...@@ -127,6 +128,11 @@ void Mixer::MixAndPublish(size_t num_max_results, const base::string16& query) { ...@@ -127,6 +128,11 @@ void Mixer::MixAndPublish(size_t num_max_results, const base::string16& query) {
if (query.empty() && non_app_ranker_) if (query.empty() && non_app_ranker_)
non_app_ranker_->OverrideZeroStateResults(&results); non_app_ranker_->OverrideZeroStateResults(&results);
// Chip results: rescore the chip results in line with app results.
if (query.empty() && chip_ranker_) {
chip_ranker_->Rank(&results);
}
std::sort(results.begin(), results.end()); std::sort(results.begin(), results.end());
const size_t original_size = results.size(); const size_t original_size = results.size();
...@@ -186,9 +192,15 @@ SearchResultRanker* Mixer::GetNonAppSearchResultRanker() { ...@@ -186,9 +192,15 @@ SearchResultRanker* Mixer::GetNonAppSearchResultRanker() {
return non_app_ranker_.get(); return non_app_ranker_.get();
} }
void Mixer::SetChipRanker(std::unique_ptr<ChipRanker> ranker) {
chip_ranker_ = std::move(ranker);
}
void Mixer::Train(const AppLaunchData& app_launch_data) { void Mixer::Train(const AppLaunchData& app_launch_data) {
if (non_app_ranker_) if (non_app_ranker_)
non_app_ranker_->Train(app_launch_data); non_app_ranker_->Train(app_launch_data);
if (chip_ranker_)
chip_ranker_->Train(app_launch_data);
} }
} // namespace app_list } // namespace app_list
...@@ -25,6 +25,7 @@ namespace test { ...@@ -25,6 +25,7 @@ namespace test {
FORWARD_DECLARE_TEST(MixerTest, Publish); FORWARD_DECLARE_TEST(MixerTest, Publish);
} }
class ChipRanker;
class SearchProvider; class SearchProvider;
class SearchResultRanker; class SearchResultRanker;
enum class RankingItemType; enum class RankingItemType;
...@@ -60,6 +61,9 @@ class Mixer { ...@@ -60,6 +61,9 @@ class Mixer {
// non-app ranking. // non-app ranking.
SearchResultRanker* GetNonAppSearchResultRanker(); SearchResultRanker* GetNonAppSearchResultRanker();
// Sets a ChipRanker to re-rank chip results before they are published.
void SetChipRanker(std::unique_ptr<ChipRanker> ranker);
// Handle a training signal. // Handle a training signal.
void Train(const AppLaunchData& app_launch_data); void Train(const AppLaunchData& app_launch_data);
...@@ -95,6 +99,7 @@ class Mixer { ...@@ -95,6 +99,7 @@ class Mixer {
// Adaptive models used for re-ranking search results. // Adaptive models used for re-ranking search results.
std::unique_ptr<SearchResultRanker> non_app_ranker_; std::unique_ptr<SearchResultRanker> non_app_ranker_;
std::unique_ptr<ChipRanker> chip_ranker_;
DISALLOW_COPY_AND_ASSIGN(Mixer); DISALLOW_COPY_AND_ASSIGN(Mixer);
}; };
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "chrome/browser/ui/app_list/search/chrome_search_result.h" #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h" #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
#include "chrome/browser/ui/app_list/search/search_provider.h" #include "chrome/browser/ui/app_list/search/search_provider.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h" #include "chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h"
...@@ -83,6 +84,10 @@ void SearchController::InitializeRankers() { ...@@ -83,6 +84,10 @@ void SearchController::InitializeRankers() {
profile_, ServiceAccessType::EXPLICIT_ACCESS)); profile_, ServiceAccessType::EXPLICIT_ACCESS));
ranker->InitializeRankers(this); ranker->InitializeRankers(this);
mixer_->SetNonAppSearchResultRanker(std::move(ranker)); mixer_->SetNonAppSearchResultRanker(std::move(ranker));
if (app_list_features::IsSuggestedFilesEnabled()) {
mixer_->SetChipRanker(std::make_unique<ChipRanker>(profile_));
}
} }
void SearchController::Start(const base::string16& query) { void SearchController::Start(const base::string16& query) {
......
...@@ -44,8 +44,10 @@ namespace { ...@@ -44,8 +44,10 @@ namespace {
// number of results to be displayed in UI. // number of results to be displayed in UI.
constexpr size_t kMaxAppsGroupResults = 7; constexpr size_t kMaxAppsGroupResults = 7;
constexpr size_t kMaxLauncherSearchResults = 2; constexpr size_t kMaxLauncherSearchResults = 2;
constexpr size_t kMaxZeroStateFileResults = 6; // We need twice as many ZeroState and Drive file results as we need
constexpr size_t kMaxDriveQuickAccessResults = 6; // duplicates of these results for the suggestion chips.
constexpr size_t kMaxZeroStateFileResults = 20;
constexpr size_t kMaxDriveQuickAccessResults = 10;
constexpr size_t kMaxAppReinstallSearchResults = 1; constexpr size_t kMaxAppReinstallSearchResults = 1;
// We show up to 6 Play Store results. However, part of Play Store results may // We show up to 6 Play Store results. However, part of Play Store results may
// be filtered out because they may correspond to already installed Web apps. So // be filtered out because they may correspond to already installed Web apps. So
......
// 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 "chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker.h"
#include <algorithm>
#include <string>
#include <utility>
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.h"
namespace app_list {
namespace {
// Apps have a boost of 8.0 + app ranker score in range [0, 1],
// hence the range of scores is [8.0, 9.0].
constexpr double kScoreHi = 9.0;
constexpr double kScoreLo = 8.0;
// Returns whether the model should be trained on this type of data.
bool ShouldTrain(RankingItemType type) {
switch (type) {
case RankingItemType::kApp:
case RankingItemType::kChip:
case RankingItemType::kZeroStateFile:
case RankingItemType::kDriveQuickAccess:
return true;
default:
return false;
}
}
double FetchScore(const std::map<std::string, float> ranks,
ChromeSearchResult* r) {
const auto it = ranks.find(NormalizeAppId(r->id()));
if (it != ranks.end())
return it->second;
return 0.0;
}
int GetNextMatchingIndex(
Mixer::SortedResults* results,
const base::RepeatingCallback<bool(const ChromeSearchResult*)>&
result_filter,
int from_index) {
int i = from_index + 1;
while (i < static_cast<int>(results->size())) {
if (result_filter.Run((*results)[i].result)) {
return i;
}
++i;
}
return -1;
}
} // namespace
ChipRanker::ChipRanker(Profile* profile) : profile_(profile) {
DCHECK(profile);
// Set up ranker model.
RecurrenceRankerConfigProto config;
config.set_min_seconds_between_saves(240u);
config.set_condition_limit(1u);
config.set_condition_decay(0.6f);
config.set_target_limit(200);
config.set_target_decay(0.9f);
config.mutable_predictor()->mutable_default_predictor();
ranker_ = std::make_unique<RecurrenceRanker>(
"", profile_->GetPath().AppendASCII("suggested_files_ranker.pb"), config,
chromeos::ProfileHelper::IsEphemeralUserProfile(profile_));
}
ChipRanker::~ChipRanker() = default;
void ChipRanker::Train(const AppLaunchData& app_launch_data) {
// ID normalisation will ensure that a file launched from the zero-state
// result list is counted as the same item as the same file launched from
// the suggestion chips.
if (ShouldTrain(app_launch_data.ranking_item_type)) {
ranker_->Record(NormalizeAppId(app_launch_data.id));
}
}
void ChipRanker::Rank(Mixer::SortedResults* results) {
std::sort(results->begin(), results->end());
const auto app_chip_filter =
base::BindRepeating([](const ChromeSearchResult* r) -> bool {
return (r->display_type() == ash::SearchResultDisplayType::kTile ||
r->display_type() == ash::SearchResultDisplayType::kChip) &&
r->is_recommendation();
});
const auto file_chip_filter =
base::BindRepeating([](const ChromeSearchResult* r) -> bool {
return r->result_type() == ash::AppListSearchResultType::kFileChip ||
r->result_type() ==
ash::AppListSearchResultType::kDriveQuickAccessChip;
});
// Use filters to find first two app chips and first file chip
int app1 = GetNextMatchingIndex(results, app_chip_filter, -1);
int app2 = GetNextMatchingIndex(results, app_chip_filter, app1);
int file = GetNextMatchingIndex(results, file_chip_filter, -1);
int prev_file = -1;
// If we couldn't find any files or couldn't find two or more apps.
if (file < 0 || app1 < 0 || app2 < 0) {
return;
}
// Fetch rankings from |ranker_|.
std::map<std::string, float> ranks = ranker_->Rank();
// Refer to class comment.
double app1_rescore = FetchScore(ranks, (*results)[app1].result);
double app2_rescore = FetchScore(ranks, (*results)[app2].result);
double file_rescore = 0.0;
double prev_file_rescore = kScoreHi;
double hi = 0.0;
double lo = 0.0;
while (file >= 0 && app1 >= 0) {
file_rescore = FetchScore(ranks, (*results)[file].result);
// File should sit above lowest of two app scores.
if (file_rescore > app2_rescore) {
// Find upper and lower bounds on score.
hi = prev_file > 0 ? (*results)[prev_file].score : kScoreHi;
lo = app2 > 0 ? (*results)[app2].score : kScoreLo;
if (prev_file_rescore > app1_rescore) {
if (file_rescore < app1_rescore)
hi = (*results)[app1].score;
else if (file_rescore > app1_rescore)
lo = (*results)[app1].score;
}
// Place new score at midpoint between hi and lo.
(*results)[file].score = lo + ((hi - lo) / 2);
prev_file = file;
file = GetNextMatchingIndex(results, file_chip_filter, file);
prev_file_rescore = file_rescore;
} else {
// File should sit below both current app scores.
app1 = app2;
app1_rescore = app2_rescore;
app2 = GetNextMatchingIndex(results, app_chip_filter, app1);
app2_rescore =
app2 < 0 ? kScoreLo : FetchScore(ranks, (*results)[app2].result);
}
}
}
void ChipRanker::SetForTest(std::unique_ptr<RecurrenceRanker> ranker) {
ranker_ = std::move(ranker);
}
} // namespace app_list
// 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 CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_CHIP_RANKER_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_CHIP_RANKER_H_
#include <map>
#include <memory>
#include <string>
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/app_list/search/mixer.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
#include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.h"
namespace app_list {
// A ChipRanker provides a method for ranking suggestion chips in the Chrome OS
// Launcher. Given a list of SortedResults from the Mixer, the ChipRanker will
// rescore the chip items so that they are appropriately ranked, while
// preserving the original ordering of all groups of results.
//
// The ranking algorithm works as follows:
// - Start with sorting the results already scored from the Mixer
// - Take the top two app items, app1 and app2
// - For each chip in the SortedResults list:
// 1. Rank app1, app2 and chip using a Dolphin model
// 2. Adjust chip score to sit in the correct position
// relative to the two apps:
// - If chip should be first
// set chip score > app1 score
// - If chip should sit between
// set chip score > app2 score, < app1 score
// - If chip is ranked last
// take app2 and the next app item, app3, and continue
// with same file.
class ChipRanker {
public:
explicit ChipRanker(Profile* profile);
~ChipRanker();
ChipRanker(const ChipRanker&) = delete;
ChipRanker& operator=(const ChipRanker&) = delete;
// Train the ranker that compares the different result types.
void Train(const AppLaunchData& app_launch_data);
// Adjusts chip scores to fit in line with app scores using
// ranking algorithm detailed above.
void Rank(Mixer::SortedResults* results);
// Set a fake ranker for tests.
void SetForTest(std::unique_ptr<RecurrenceRanker> ranker);
// Ranker generates scores used for re-arranging items, not
// raw result scores.
std::unique_ptr<RecurrenceRanker> ranker_;
private:
Profile* profile_;
};
} // namespace app_list
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_CHIP_RANKER_H_
...@@ -23,6 +23,7 @@ ZeroStateResultType ZeroStateTypeFromRankingType( ...@@ -23,6 +23,7 @@ ZeroStateResultType ZeroStateTypeFromRankingType(
case RankingItemType::kFile: case RankingItemType::kFile:
case RankingItemType::kApp: case RankingItemType::kApp:
case RankingItemType::kArcAppShortcut: case RankingItemType::kArcAppShortcut:
case RankingItemType::kChip:
return ZeroStateResultType::kUnanticipated; return ZeroStateResultType::kUnanticipated;
case RankingItemType::kOmniboxGeneric: case RankingItemType::kOmniboxGeneric:
return ZeroStateResultType::kOmniboxSearch; return ZeroStateResultType::kOmniboxSearch;
......
...@@ -33,12 +33,13 @@ RankingItemType RankingItemTypeFromSearchResult( ...@@ -33,12 +33,13 @@ RankingItemType RankingItemTypeFromSearchResult(
return RankingItemType::kIgnored; return RankingItemType::kIgnored;
case ash::AppListSearchResultType::kArcAppShortcut: case ash::AppListSearchResultType::kArcAppShortcut:
return RankingItemType::kArcAppShortcut; return RankingItemType::kArcAppShortcut;
case ash::AppListSearchResultType::kFileChip:
case ash::AppListSearchResultType::kZeroStateFile: case ash::AppListSearchResultType::kZeroStateFile:
return RankingItemType::kZeroStateFile; return RankingItemType::kZeroStateFile;
case ash::AppListSearchResultType::kDriveQuickAccessChip:
case ash::AppListSearchResultType::kDriveQuickAccess: case ash::AppListSearchResultType::kDriveQuickAccess:
return RankingItemType::kDriveQuickAccess; return RankingItemType::kDriveQuickAccess;
case ash::AppListSearchResultType::kFileChip:
case ash::AppListSearchResultType::kDriveQuickAccessChip:
return RankingItemType::kChip;
} }
} }
......
...@@ -24,6 +24,7 @@ enum class RankingItemType { ...@@ -24,6 +24,7 @@ enum class RankingItemType {
kArcAppShortcut = 5, kArcAppShortcut = 5,
kZeroStateFile = 6, kZeroStateFile = 6,
kDriveQuickAccess = 7, kDriveQuickAccess = 7,
kChip = 8,
}; };
// Convert a |ChromeSearchResult| into its |RankingItemType|. // Convert a |ChromeSearchResult| into its |RankingItemType|.
......
...@@ -4,11 +4,13 @@ ...@@ -4,11 +4,13 @@
#include "chrome/browser/ui/app_list/search/zero_state_file_provider.h" #include "chrome/browser/ui/app_list/search/zero_state_file_provider.h"
#include "ash/public/cpp/app_list/app_list_features.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/search/zero_state_file_result.h" #include "chrome/browser/ui/app_list/search/zero_state_file_result.h"
...@@ -20,6 +22,7 @@ ...@@ -20,6 +22,7 @@
namespace app_list { namespace app_list {
namespace { namespace {
using base::test::ScopedFeatureList;
using ::file_manager::file_tasks::FileTasksObserver; using ::file_manager::file_tasks::FileTasksObserver;
using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAre;
...@@ -57,6 +60,7 @@ class ZeroStateFileProviderTest : public testing::Test { ...@@ -57,6 +60,7 @@ class ZeroStateFileProviderTest : public testing::Test {
void Wait() { task_environment_.RunUntilIdle(); } void Wait() { task_environment_.RunUntilIdle(); }
content::BrowserTaskEnvironment task_environment_; content::BrowserTaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<Profile> profile_; std::unique_ptr<Profile> profile_;
std::unique_ptr<ZeroStateFileProvider> provider_; std::unique_ptr<ZeroStateFileProvider> provider_;
...@@ -68,7 +72,11 @@ TEST_F(ZeroStateFileProviderTest, NoResultsWithQuery) { ...@@ -68,7 +72,11 @@ TEST_F(ZeroStateFileProviderTest, NoResultsWithQuery) {
EXPECT_TRUE(provider_->results().empty()); EXPECT_TRUE(provider_->results().empty());
} }
TEST_F(ZeroStateFileProviderTest, Simple) { TEST_F(ZeroStateFileProviderTest, ResultsProvided) {
// Disable flag.
scoped_feature_list_.InitWithFeatures(
{}, {app_list_features::kEnableSuggestedFiles});
WriteFile("exists_1.txt"); WriteFile("exists_1.txt");
WriteFile("exists_2.png"); WriteFile("exists_2.png");
WriteFile("exists_3.pdf"); WriteFile("exists_3.pdf");
...@@ -85,4 +93,27 @@ TEST_F(ZeroStateFileProviderTest, Simple) { ...@@ -85,4 +93,27 @@ TEST_F(ZeroStateFileProviderTest, Simple) {
UnorderedElementsAre(Title("exists_1.txt"), Title("exists_2.png"))); UnorderedElementsAre(Title("exists_1.txt"), Title("exists_2.png")));
} }
TEST_F(ZeroStateFileProviderTest, ResultsProvidedWithChips) {
// Enable flag - with flag enabled, we expect to receive the chip
// results for each file as well, so each file should be listed twice.
scoped_feature_list_.InitWithFeatures(
{app_list_features::kEnableSuggestedFiles}, {});
WriteFile("exists_1.txt");
WriteFile("exists_2.png");
WriteFile("exists_3.pdf");
provider_->OnFilesOpened(
{OpenEvent("exists_1.txt"), OpenEvent("exists_2.png")});
provider_->OnFilesOpened({OpenEvent("nonexistant.txt")});
provider_->Start(base::string16());
Wait();
EXPECT_THAT(
provider_->results(),
UnorderedElementsAre(Title("exists_1.txt"), Title("exists_2.png"),
Title("exists_1.txt"), Title("exists_2.png")));
}
} // namespace app_list } // namespace app_list
...@@ -4416,6 +4416,7 @@ test("unit_tests") { ...@@ -4416,6 +4416,7 @@ test("unit_tests") {
"../browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc", "../browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc",
"../browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc", "../browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc",
"../browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc", "../browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc",
"../browser/ui/app_list/search/search_result_ranker/chip_ranker_unittest.cc",
"../browser/ui/app_list/search/search_result_ranker/frecency_store_unittest.cc", "../browser/ui/app_list/search/search_result_ranker/frecency_store_unittest.cc",
"../browser/ui/app_list/search/search_result_ranker/histogram_util_unittest.cc", "../browser/ui/app_list/search/search_result_ranker/histogram_util_unittest.cc",
"../browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc", "../browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc",
......
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