// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/birch/birch_model.h"
#include <optional>
#include "ash/birch/birch_data_provider.h"
#include "ash/birch/birch_item.h"
#include "ash/birch/birch_item_remover.h"
#include "ash/birch/stub_birch_client.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/geolocation_access_level.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
#include "ash/public/cpp/test/test_image_downloader.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/files/scoped_temp_dir.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/components/geolocation/simple_geolocation_provider.h"
#include "components/prefs/pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace {
// Returns true if `items` contains BirchItems of each of `types` in any order.
bool HasItemTypes(const std::vector<std::unique_ptr<BirchItem>>& items,
const std::vector<BirchItemType>& types) {
return base::ranges::all_of(types, [&](BirchItemType type) {
return base::ranges::any_of(items,
[&](const std::unique_ptr<BirchItem>& item) {
return item->GetType() == type;
});
});
}
std::vector<BirchFileItem> MakeFileItemList(int item_count) {
std::vector<BirchFileItem> file_item_list;
for (int i = 0; i < item_count; i++) {
file_item_list.emplace_back(
base::FilePath("test path " + base::NumberToString(i)), "title",
u"suggestion", base::Time(), "file_id_" + base::NumberToString(i),
"icon_url");
}
return file_item_list;
}
std::vector<BirchCalendarItem> MakeCalendarItemList(int event_count) {
std::vector<BirchCalendarItem> calendar_item_list;
for (int i = 0; i < event_count; i++) {
calendar_item_list.emplace_back(
/*title=*/u"Event " + base::NumberToString16(i),
/*start_time=*/base::Time(),
/*end_time=*/base::Time(),
/*calendar_url=*/GURL(),
/*conference_url=*/GURL(),
/*event_id=*/"event_id_" + base::NumberToString(i),
/*all_day_event=*/false);
}
return calendar_item_list;
}
std::vector<BirchAttachmentItem> MakeAttachmentItemList(int item_count) {
std::vector<BirchAttachmentItem> attachment_item_list;
for (int i = 0; i < item_count; i++) {
attachment_item_list.emplace_back(
u"Attachment " + base::NumberToString16(i),
/*file_url=*/GURL(),
/*icon_url=*/GURL(),
/*start_time=*/base::Time(),
/*end_time=*/base::Time(),
/*file_id=*/"file_id" + base::NumberToString(i));
}
return attachment_item_list;
}
std::vector<BirchTabItem> MakeTabItemList(int count) {
std::vector<BirchTabItem> items;
for (int i = 0; i < count; i++) {
items.emplace_back(u"tab " + base::NumberToString16(i),
GURL("https://www.example.com/"), base::Time(),
GURL("https://www.favicon.com/"), "session",
BirchTabItem::DeviceFormFactor::kDesktop);
}
return items;
}
class TestModelConsumer {
public:
void OnItemsReady(const std::string& id) {
items_ready_responses_.push_back(id);
}
const std::vector<std::string> items_ready_responses() const {
return items_ready_responses_;
}
private:
std::vector<std::string> items_ready_responses_;
};
base::Time TimeFromString(const char* time_string) {
base::Time time;
CHECK(base::Time::FromString(time_string, &time));
return time;
}
class TestModelObserver : public BirchModel::Observer {
public:
TestModelObserver() { Shell::Get()->birch_model()->AddObserver(this); }
~TestModelObserver() override {
Shell::Get()->birch_model()->RemoveObserver(this);
}
void OnBirchClientSet() override { birch_client_set_ = true; }
bool birch_client_set() const { return birch_client_set_; }
private:
bool birch_client_set_ = false;
};
} // namespace
class BirchModelTest : public AshTestBase {
public:
BirchModelTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
feature_list_.InitWithFeatures(
{features::kForestFeature, features::kBirchWeather,
features::kBirchVideoConferenceSuggestions},
{});
}
void SetUp() override {
AshTestBase::SetUp();
// Inject no-op, stub weather provider to prevent real implementation from
// returning empty weather info.
stub_birch_client_.InstallStubWeatherDataProvider();
Shell::Get()->birch_model()->SetClientAndInit(&stub_birch_client_);
base::RunLoop run_loop;
Shell::Get()
->birch_model()
->GetItemRemoverForTest()
->SetProtoInitCallbackForTest(run_loop.QuitClosure());
run_loop.Run();
// Set a test clock so that ranking uses a consistent time across test runs.
test_clock_.SetNow(TimeFromString("22 Feb 2024 4:00 UTC"));
Shell::Get()->birch_model()->OverrideClockForTest(&test_clock_);
}
void TearDown() override {
Shell::Get()->birch_model()->SetClientAndInit(nullptr);
AshTestBase::TearDown();
}
void RecordProviderHiddenHistograms() {
Shell::Get()->birch_model()->RecordProviderHiddenHistograms();
}
// Disables all data type prefs except the given exceptions.
void DisableAllDataTypePrefsExcept(std::vector<const char*> exceptions) {
PrefService* pref_service =
Shell::Get()->session_controller()->GetPrimaryUserPrefService();
ASSERT_TRUE(pref_service);
const char* kDataPrefs[] = {
prefs::kBirchUseCalendar, prefs::kBirchUseFileSuggest,
prefs::kBirchUseChromeTabs, prefs::kBirchUseLostMedia,
prefs::kBirchUseReleaseNotes, prefs::kBirchUseWeather,
prefs::kBirchUseCoral,
};
for (const char* pref : kDataPrefs) {
bool enable = false;
for (const char* exception : exceptions) {
/*strcmp returns 0 when inputs are the same*/
if (0 == strcmp(pref, exception)) {
enable = true;
}
}
pref_service->SetBoolean(pref, enable);
}
}
protected:
base::test::ScopedFeatureList feature_list_;
StubBirchClient stub_birch_client_;
base::SimpleTestClock test_clock_;
};
class BirchModelWithoutWeatherTest : public AshTestBase {
public:
BirchModelWithoutWeatherTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
feature_list_.InitWithFeatures({features::kForestFeature},
{features::kBirchWeather});
}
void SetUp() override {
AshTestBase::SetUp();
Shell::Get()->birch_model()->SetClientAndInit(&stub_birch_client_);
base::RunLoop run_loop;
Shell::Get()
->birch_model()
->GetItemRemoverForTest()
->SetProtoInitCallbackForTest(run_loop.QuitClosure());
run_loop.Run();
}
void TearDown() override {
Shell::Get()->birch_model()->SetClientAndInit(nullptr);
AshTestBase::TearDown();
}
protected:
base::test::ScopedFeatureList feature_list_;
StubBirchClient stub_birch_client_;
};
// Test that requesting data and adding all fresh items to the model will run
// the callback.
TEST_F(BirchModelTest, AddItemNotifiesCallback) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
// Setting items in the model does not notify when no request has occurred.
model->SetCalendarItems(std::vector<BirchCalendarItem>());
model->SetAttachmentItems(std::vector<BirchAttachmentItem>());
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems(std::vector<BirchLastActiveItem>());
model->SetMostVisitedItems(std::vector<BirchMostVisitedItem>());
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems(std::vector<BirchLostMediaItem>());
model->SetFileSuggestItems(std::vector<BirchFileItem>());
model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Make a data fetch request and set fresh tab data.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems(std::vector<BirchLastActiveItem>());
model->SetMostVisitedItems(std::vector<BirchMostVisitedItem>());
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems(std::vector<BirchLostMediaItem>());
// Consumer is not notified until all data sources have responded.
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetWeatherItems({});
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
// Adding file items sets all data as fresh, notifying consumers.
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Setting the file suggest items should not trigger items ready again, since
// no data fetch was requested.
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/2));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Request another data fetch and expect the consumer to be notified once
// items are set again.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"1"));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems(std::vector<BirchLastActiveItem>());
model->SetMostVisitedItems(std::vector<BirchMostVisitedItem>());
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems(std::vector<BirchLostMediaItem>());
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/2));
model->SetWeatherItems({});
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0", "1"));
}
TEST_F(BirchModelTest, RequestBirchDataFetchRecordsHistograms) {
base::HistogramTester histograms;
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
// Make a data fetch request.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
// Simulate each data provider replying.
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
// Callback is called.
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Histograms were recorded for each type.
histograms.ExpectTotalCount("Ash.Birch.Latency.Calendar", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.File", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.Tab", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.LastActive", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.MostVisited", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.SelfShare", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.Weather", 1);
histograms.ExpectTotalCount("Ash.Birch.Latency.ReleaseNotes", 1);
// Total latency was recorded.
histograms.ExpectTotalCount("Ash.Birch.TotalLatency", 1);
// Simulate a data provider replying outside of a fetch.
model->SetFileSuggestItems({});
// Histograms didn't change.
histograms.ExpectTotalCount("Ash.Birch.Latency.File", 1);
histograms.ExpectTotalCount("Ash.Birch.TotalLatency", 1);
}
TEST_F(BirchModelTest, RequestBirchDataFetchRecordsTotalLatencyHistogram) {
base::HistogramTester histograms;
BirchModel* model = Shell::Get()->birch_model();
// Make a data fetch request for post-login.
model->RequestBirchDataFetch(/*is_post_login=*/true, base::DoNothing());
// Simulate each data provider replying.
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
// Total latency post login was recorded.
histograms.ExpectTotalCount("Ash.Birch.TotalLatencyPostLogin", 1);
// Make a data fetch request for non-post-login.
model->RequestBirchDataFetch(/*is_post_login=*/false, base::DoNothing());
// Simulate each data provider replying.
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
// Regular latency histogram was recorded.
histograms.ExpectTotalCount("Ash.Birch.TotalLatency", 1);
}
TEST_F(BirchModelTest, DataFetchForNonPrimaryUserClearsModel) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
// Sign in to a secondary user.
SimulateUserLogin("[email protected]");
ASSERT_FALSE(Shell::Get()->session_controller()->IsUserPrimary());
// Add an item to the model.
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
// Request a data fetch.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
// The fetch callback was called.
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// The model is empty.
EXPECT_TRUE(model->GetAllItems().empty());
}
TEST_F(BirchModelTest, DisablingAllPrefsCausesNoFetch) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
// Set all the data types so the data is considered fresh.
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetFileSuggestItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
ASSERT_TRUE(model->IsDataFresh());
// Disable all the prefs.
DisableAllDataTypePrefsExcept(std::vector<const char*>());
// Install a stub weather provider.
auto* weather_provider = stub_birch_client_.InstallStubWeatherDataProvider();
// Request a data fetch.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
// The fetch callback was called immediately because nothing was fetched.
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Nothing was fetched and the (empty) data is still fresh.
auto& client = stub_birch_client_;
EXPECT_FALSE(client.DidRequestCalendarDataFetch());
EXPECT_FALSE(client.DidRequestFileSuggestDataFetch());
EXPECT_FALSE(client.DidRequestRecentTabsDataFetch());
EXPECT_FALSE(client.DidRequestLastActiveDataFetch());
EXPECT_FALSE(client.DidRequestMostVisitedDataFetch());
EXPECT_FALSE(client.DidRequestSelfShareDataFetch());
EXPECT_FALSE(client.DidRequestLostMediaDataFetch());
EXPECT_FALSE(client.DidRequestReleaseNotesDataFetch());
EXPECT_FALSE(weather_provider->did_request_birch_data_fetch());
EXPECT_TRUE(model->IsDataFresh());
}
TEST_F(BirchModelTest, EnablingOnePrefsCausesFetch) {
BirchModel* model = Shell::Get()->birch_model();
// Disable all the prefs except calendar.
DisableAllDataTypePrefsExcept(
std::vector<const char*>{prefs::kBirchUseCalendar});
// Install a stub weather provider.
auto* weather_provider = stub_birch_client_.InstallStubWeatherDataProvider();
// Request a fetch.
model->RequestBirchDataFetch(/*is_post_login=*/false, base::DoNothing());
// Only calendar was fetched.
auto& client = stub_birch_client_;
EXPECT_TRUE(client.DidRequestCalendarDataFetch());
EXPECT_FALSE(client.DidRequestFileSuggestDataFetch());
EXPECT_FALSE(client.DidRequestRecentTabsDataFetch());
EXPECT_FALSE(client.DidRequestLastActiveDataFetch());
EXPECT_FALSE(client.DidRequestMostVisitedDataFetch());
EXPECT_FALSE(client.DidRequestSelfShareDataFetch());
EXPECT_FALSE(client.DidRequestLostMediaDataFetch());
EXPECT_FALSE(client.DidRequestReleaseNotesDataFetch());
EXPECT_FALSE(weather_provider->did_request_birch_data_fetch());
}
TEST_F(BirchModelTest, DisablingPrefsClearsModel) {
BirchModel* model = Shell::Get()->birch_model();
// Populate the model with every data type.
model->SetCalendarItems(MakeCalendarItemList(/*event_count=*/1));
model->SetAttachmentItems(MakeAttachmentItemList(/*item_count=*/1));
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetRecentTabItems(MakeTabItemList(/*count=*/1));
std::vector<BirchLastActiveItem> last_active_list;
last_active_list.emplace_back(u"active", GURL("https://yahoo.com/"),
base::Time());
model->SetLastActiveItems(std::move(last_active_list));
std::vector<BirchMostVisitedItem> most_visited_list;
most_visited_list.emplace_back(u"visited", GURL("https://google.com/"));
model->SetMostVisitedItems(std::move(most_visited_list));
std::vector<BirchSelfShareItem> self_share_item_list;
self_share_item_list.emplace_back(
u"self share guid", u"self share tab", GURL("https://www.example.com/"),
base::Time(), u"my device", SecondaryIconType::kTabFromDesktop,
base::DoNothing());
model->SetSelfShareItems(std::move(self_share_item_list));
std::vector<BirchWeatherItem> weather_item_list;
weather_item_list.emplace_back(u"cloudy", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_item_list));
std::vector<BirchReleaseNotesItem> release_notes_item_list;
release_notes_item_list.emplace_back(
u"note", u"explore", GURL("https://www.example.com/"), base::Time());
model->SetReleaseNotesItems(release_notes_item_list);
std::vector<BirchLostMediaItem> lost_media_item_list;
lost_media_item_list.emplace_back(
GURL("https://www.source.com/"), u"media title", std::nullopt,
SecondaryIconType::kLostMediaVideo, base::DoNothing());
model->SetLostMediaItems(lost_media_item_list);
ASSERT_TRUE(model->IsDataFresh());
// Disable all the prefs for data providers.
DisableAllDataTypePrefsExcept(std::vector<const char*>());
// The model is now empty.
EXPECT_TRUE(model->GetAllItems().empty());
EXPECT_TRUE(model->GetCalendarItemsForTest().empty());
EXPECT_TRUE(model->GetAttachmentItemsForTest().empty());
EXPECT_TRUE(model->GetFileSuggestItemsForTest().empty());
EXPECT_TRUE(model->GetTabsForTest().empty());
EXPECT_TRUE(model->GetLastActiveItemsForTest().empty());
EXPECT_TRUE(model->GetMostVisitedItemsForTest().empty());
EXPECT_TRUE(model->GetSelfShareItemsForTest().empty());
EXPECT_TRUE(model->GetLostMediaItemsForTest().empty());
EXPECT_TRUE(model->GetWeatherForTest().empty());
EXPECT_TRUE(model->GetReleaseNotesItemsForTest().empty());
}
TEST_F(BirchModelTest, GetAllItemsDoesNotReturnItemsWithDisabledPrefs) {
BirchModel* model = Shell::Get()->birch_model();
// Disable all the prefs for data providers.
DisableAllDataTypePrefsExcept(std::vector<const char*>());
// Populate the model with every data type.
model->SetCalendarItems(MakeCalendarItemList(/*event_count=*/1));
model->SetAttachmentItems(MakeAttachmentItemList(/*item_count=*/1));
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetRecentTabItems(MakeTabItemList(/*count=*/1));
std::vector<BirchLastActiveItem> last_active_list;
last_active_list.emplace_back(u"active", GURL("https://yahoo.com/"),
base::Time());
model->SetLastActiveItems(std::move(last_active_list));
std::vector<BirchMostVisitedItem> most_visited_list;
most_visited_list.emplace_back(u"visited", GURL("https://google.com/"));
model->SetMostVisitedItems(std::move(most_visited_list));
std::vector<BirchSelfShareItem> self_share_item_list;
self_share_item_list.emplace_back(
u"self share guid", u"self share tab", GURL("https://www.example.com/"),
base::Time(), u"my device", SecondaryIconType::kTabFromDesktop,
base::DoNothing());
model->SetSelfShareItems(std::move(self_share_item_list));
std::vector<BirchWeatherItem> weather_item_list;
weather_item_list.emplace_back(u"cloudy", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_item_list));
std::vector<BirchReleaseNotesItem> release_notes_item_list;
release_notes_item_list.emplace_back(
u"note", u"explore", GURL("https://www.example.com/"), base::Time());
model->SetReleaseNotesItems(release_notes_item_list);
std::vector<BirchLostMediaItem> lost_media_item_list;
lost_media_item_list.emplace_back(
GURL("https://www.source.com/"), u"media title", std::nullopt,
SecondaryIconType::kLostMediaVideo, base::DoNothing());
model->SetLostMediaItems(lost_media_item_list);
// The model returns no items.
EXPECT_EQ(model->GetAllItems().size(), 0u);
}
TEST_F(BirchModelTest, DisablingPrefsMarksDataFresh) {
BirchModel* model = Shell::Get()->birch_model();
ASSERT_FALSE(model->IsDataFresh());
// Disable all the prefs for data providers.
DisableAllDataTypePrefsExcept(std::vector<const char*>());
// The data is reported as fresh.
EXPECT_TRUE(model->IsDataFresh());
}
TEST_F(BirchModelTest, DisablingCalendarPrefBlocksSetCalendarItems) {
BirchModel* model = Shell::Get()->birch_model();
PrefService* prefs =
Shell::Get()->session_controller()->GetPrimaryUserPrefService();
prefs->SetBoolean(prefs::kBirchUseCalendar, false);
// Setting the items is blocked by the pref.
model->SetCalendarItems(MakeCalendarItemList(/*event_count=*/1));
EXPECT_TRUE(model->GetCalendarItemsForTest().empty());
}
TEST_F(BirchModelTest, DisablingFileSuggestPrefBlocksSetAttachmentItems) {
BirchModel* model = Shell::Get()->birch_model();
PrefService* prefs =
Shell::Get()->session_controller()->GetPrimaryUserPrefService();
prefs->SetBoolean(prefs::kBirchUseFileSuggest, false);
// Setting the items is blocked by the pref.
model->SetAttachmentItems(MakeAttachmentItemList(/*item_count=*/1));
EXPECT_TRUE(model->GetAttachmentItemsForTest().empty());
}
TEST_F(BirchModelTest, FetchWithOnePrefDisabledMarksDataFresh) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
ASSERT_FALSE(model->IsDataFresh());
// Disable the weather data type via prefs.
PrefService* prefs =
Shell::Get()->session_controller()->GetPrimaryUserPrefService();
ASSERT_TRUE(prefs);
prefs->SetBoolean(prefs::kBirchUseWeather, false);
// Request a fetch.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
// Reply with everything but weather.
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetFileSuggestItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetReleaseNotesItems({});
// Consumer was notified that fetch was complete.
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Data is fresh.
EXPECT_TRUE(model->IsDataFresh());
}
TEST_F(BirchModelTest, EnablePrefsDuringFetchCausesDataFetchRequest) {
BirchModel* model = Shell::Get()->birch_model();
// Disable all the prefs except weather, so that a data fetch request creates
// a pending request.
DisableAllDataTypePrefsExcept(
std::vector<const char*>({prefs::kBirchUseWeather}));
// Request a fetch, creating a pending fetch request.
model->RequestBirchDataFetch(/*is_post_login=*/false, base::DoNothing());
auto& client = stub_birch_client_;
EXPECT_FALSE(client.DidRequestCalendarDataFetch());
EXPECT_FALSE(client.DidRequestFileSuggestDataFetch());
EXPECT_FALSE(client.DidRequestRecentTabsDataFetch());
EXPECT_FALSE(client.DidRequestLastActiveDataFetch());
EXPECT_FALSE(client.DidRequestMostVisitedDataFetch());
EXPECT_FALSE(client.DidRequestSelfShareDataFetch());
EXPECT_FALSE(client.DidRequestLostMediaDataFetch());
EXPECT_FALSE(client.DidRequestReleaseNotesDataFetch());
// Enable prefs and then expect that data fetch requests are called for each
// enabled data type.
DisableAllDataTypePrefsExcept(std::vector<const char*>(
{prefs::kBirchUseCalendar, prefs::kBirchUseFileSuggest,
prefs::kBirchUseChromeTabs, prefs::kBirchUseLostMedia,
prefs::kBirchUseReleaseNotes}));
EXPECT_TRUE(client.DidRequestCalendarDataFetch());
EXPECT_TRUE(client.DidRequestFileSuggestDataFetch());
EXPECT_TRUE(client.DidRequestRecentTabsDataFetch());
EXPECT_TRUE(client.DidRequestLastActiveDataFetch());
EXPECT_TRUE(client.DidRequestMostVisitedDataFetch());
EXPECT_TRUE(client.DidRequestSelfShareDataFetch());
EXPECT_TRUE(client.DidRequestLostMediaDataFetch());
EXPECT_TRUE(client.DidRequestReleaseNotesDataFetch());
}
TEST_F(BirchModelTest, EnableWeatherPrefDuringFetchCausesDataFetchRequest) {
BirchModel* model = Shell::Get()->birch_model();
// Install a stub weather provider.
auto* weather_provider = stub_birch_client_.InstallStubWeatherDataProvider();
// Disable the weather pref.
PrefService* prefs =
Shell::Get()->session_controller()->GetPrimaryUserPrefService();
ASSERT_TRUE(prefs);
prefs->SetBoolean(prefs::kBirchUseWeather, false);
// Request a fetch, creating a pending fetch request.
model->RequestBirchDataFetch(/*is_post_login=*/false, base::DoNothing());
EXPECT_FALSE(weather_provider->did_request_birch_data_fetch());
// Enable the weather pref and expect a weather data fetch.
prefs->SetBoolean(prefs::kBirchUseWeather, true);
EXPECT_TRUE(weather_provider->did_request_birch_data_fetch());
}
// Regression test for missing attachment type check in IsDataFresh().
TEST_F(BirchModelTest, IsDataFresh_Attachments) {
BirchModel* model = Shell::Get()->birch_model();
ASSERT_FALSE(model->IsDataFresh());
// Provide all data types except attachments. Data should not be fresh.
model->SetCalendarItems({});
model->SetFileSuggestItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
EXPECT_FALSE(model->IsDataFresh());
// Providing attachments finishes the set and the data is fresh.
model->SetAttachmentItems({});
EXPECT_TRUE(model->IsDataFresh());
}
// TODO(https://crbug.com/324963992): Fix `BirchModel*Test.DataFetchTimeout`
// for debug builds.
#if defined(NDEBUG)
#define MAYBE_DataFetchTimeout DataFetchTimeout
#else
#define MAYBE_DataFetchTimeout DISABLED_DataFetchTimeout
#endif
// Test that consumer is notified when waiting a set amount of time after
// requesting birch data.
TEST_F(BirchModelTest, MAYBE_DataFetchTimeout) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
// Passing time and setting data before requesting a birch data fetch will
// not notify consumer.
task_environment()->FastForwardBy(base::Milliseconds(1000));
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetSelfShareItems({});
model->SetLostMediaItems({});
std::vector<BirchWeatherItem> weather_items;
weather_items.emplace_back(u"cloudy", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_items));
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
EXPECT_TRUE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
EXPECT_FALSE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Test that passing a short amount of time and setting some data does not
// notify that items are ready.
task_environment()->FastForwardBy(base::Milliseconds(500));
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab title", GURL("example.com"),
base::Time::Now(), GURL("example.com/favicon_url"),
"session_name",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(tab_item_list);
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Test that passing enough time notifies that items are ready.
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
EXPECT_EQ(all_items.size(), 3u);
EXPECT_TRUE(HasItemTypes(all_items, {
BirchItemType::kWeather,
BirchItemType::kFile,
BirchItemType::kTab,
}));
EXPECT_FALSE(model->IsDataFresh());
}
TEST_F(BirchModelWithoutWeatherTest, MAYBE_DataFetchTimeout) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
// Passing time and setting data before requesting a birch data fetch will
// not notify consumer.
task_environment()->FastForwardBy(base::Milliseconds(1000));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems({});
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
EXPECT_TRUE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
EXPECT_FALSE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Test that passing a short amount of time and setting some data does not
// notify that items are ready.
task_environment()->FastForwardBy(base::Milliseconds(500));
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab title", GURL("example.com"),
base::Time::Now(), GURL("example.com/favicon_url"),
"session_name",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(tab_item_list);
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Test that passing enough time notifies that items are ready.
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
EXPECT_EQ(all_items.size(), 2u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kTab);
EXPECT_EQ(all_items[1]->GetType(), BirchItemType::kFile);
EXPECT_FALSE(model->IsDataFresh());
}
// Test that the data fetch timeout is longer when requesting directly after
// login.
TEST_F(BirchModelTest, PostLoginDataFetchTimeout) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
// Passing time and setting data before requesting a birch data fetch will
// not notify consumer.
task_environment()->FastForwardBy(base::Milliseconds(1000));
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems({});
model->SetWeatherItems({});
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
EXPECT_TRUE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->RequestBirchDataFetch(/*is_post_login=*/true,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
EXPECT_FALSE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Test that passing a short amount of time and setting some data does not
// notify that items are ready.
task_environment()->FastForwardBy(base::Milliseconds(2500));
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab title", GURL("example.com"),
base::Time::Now(), GURL("example.com/favicon_url"),
"session_name",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(tab_item_list);
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
std::vector<BirchSelfShareItem> self_share_item_list;
self_share_item_list.emplace_back(
u"self share guid", u"self share tab", GURL("foo.bar.two"), base::Time(),
u"my device", SecondaryIconType::kTabFromDesktop, base::DoNothing());
model->SetSelfShareItems(std::move(self_share_item_list));
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Test that passing enough time notifies that items are ready.
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
EXPECT_EQ(all_items.size(), 2u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kFile);
EXPECT_EQ(all_items[1]->GetType(), BirchItemType::kTab);
EXPECT_FALSE(model->IsDataFresh());
}
TEST_F(BirchModelWithoutWeatherTest, AddItemNotifiesCallback) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
// Setting items in the model does not notify when no request has occurred.
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems({});
model->SetMostVisitedItems(std::vector<BirchMostVisitedItem>());
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems(std::vector<BirchLostMediaItem>());
model->SetFileSuggestItems(std::vector<BirchFileItem>());
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
// Make a data fetch request and set fresh tab data.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems({});
model->SetMostVisitedItems(std::vector<BirchMostVisitedItem>());
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems(std::vector<BirchLostMediaItem>());
// Consumer is not notified until all data sources have responded.
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
model->SetWeatherItems({});
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
// Adding file items sets all data as fresh, notifying consumers.
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Setting the file suggest items should not trigger items ready again, since
// no data fetch was requested.
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/2));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
// Request another data fetch and expect the consumer to be notified once
// items are set again.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"1"));
model->SetRecentTabItems(std::vector<BirchTabItem>());
model->SetLastActiveItems({});
model->SetMostVisitedItems(std::vector<BirchMostVisitedItem>());
model->SetSelfShareItems(std::vector<BirchSelfShareItem>());
model->SetLostMediaItems(std::vector<BirchLostMediaItem>());
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/2));
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0", "1"));
}
TEST_F(BirchModelTest, MultipleRequestsHaveIndependentTimeouts) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"1"));
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0", "1"));
EXPECT_FALSE(model->IsDataFresh());
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"2"));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0", "1"));
task_environment()->FastForwardBy(base::Milliseconds(1000));
EXPECT_THAT(consumer.items_ready_responses(),
testing::ElementsAre("0", "1", "2"));
EXPECT_FALSE(model->IsDataFresh());
}
TEST_F(BirchModelTest, ResponseAfterFirstTimeout) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
EXPECT_TRUE(model);
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::IsEmpty());
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"1"));
task_environment()->FastForwardBy(base::Milliseconds(500));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
task_environment()->FastForwardBy(base::Milliseconds(100));
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0"));
EXPECT_FALSE(model->IsDataFresh());
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
std::vector<BirchWeatherItem> weather_item_list;
weather_item_list.emplace_back(u"cloudy", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_item_list));
model->SetRecentTabItems(MakeTabItemList(/*count=*/1));
std::vector<BirchLastActiveItem> last_active_list;
last_active_list.emplace_back(u"active", GURL("https://yahoo.com/"),
base::Time());
model->SetLastActiveItems(std::move(last_active_list));
std::vector<BirchMostVisitedItem> most_visited_list;
most_visited_list.emplace_back(u"visited", GURL("https://google.com/"));
model->SetMostVisitedItems(std::move(most_visited_list));
std::vector<BirchSelfShareItem> self_share_item_list;
self_share_item_list.emplace_back(
u"self share guid", u"self share tab", GURL("foo.bar.two"), base::Time(),
u"my device", SecondaryIconType::kTabFromDesktop, base::DoNothing());
model->SetSelfShareItems(std::move(self_share_item_list));
model->SetCalendarItems(MakeCalendarItemList(/*event_count=*/1));
model->SetAttachmentItems(MakeAttachmentItemList(/*item_count=*/1));
std::vector<BirchReleaseNotesItem> release_notes_item_list;
release_notes_item_list.emplace_back(
u"note", u"explore", GURL("https://www.example.com/"), base::Time());
model->SetReleaseNotesItems(release_notes_item_list);
std::vector<BirchLostMediaItem> lost_media_item_list;
lost_media_item_list.emplace_back(
GURL("https://www.source.com/"), u"media title", std::nullopt,
SecondaryIconType::kLostMediaVideo, base::DoNothing());
model->SetLostMediaItems(lost_media_item_list);
EXPECT_TRUE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0", "1"));
EXPECT_EQ(model->GetAllItems().size(), 10u);
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"2"));
EXPECT_FALSE(model->IsDataFresh());
task_environment()->FastForwardBy(base::Milliseconds(100));
EXPECT_FALSE(model->IsDataFresh());
EXPECT_THAT(consumer.items_ready_responses(), testing::ElementsAre("0", "1"));
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetReleaseNotesItems({});
EXPECT_THAT(consumer.items_ready_responses(),
testing::ElementsAre("0", "1", "2"));
EXPECT_EQ(model->GetAllItems().size(), 0u);
EXPECT_TRUE(model->IsDataFresh());
}
TEST_F(BirchModelTest, GetAllItems) {
BirchModel* model = Shell::Get()->birch_model();
// Insert one item of each type.
std::vector<BirchWeatherItem> weather_item_list;
weather_item_list.emplace_back(u"cloudy", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_item_list));
std::vector<BirchReleaseNotesItem> release_notes_item_list;
release_notes_item_list.emplace_back(
u"note", u"explore", GURL("https://www.example.com/"), base::Time());
model->SetReleaseNotesItems(std::move(release_notes_item_list));
model->SetCalendarItems(MakeCalendarItemList(/*event_count=*/1));
model->SetAttachmentItems(MakeAttachmentItemList(/*item_count=*/1));
model->SetRecentTabItems(MakeTabItemList(/*count=*/1));
std::vector<BirchLastActiveItem> last_active_list;
last_active_list.emplace_back(u"active", GURL("https://yahoo.com/"),
base::Time());
model->SetLastActiveItems(std::move(last_active_list));
std::vector<BirchMostVisitedItem> most_visited_list;
most_visited_list.emplace_back(u"visited", GURL("https://google.com/"));
model->SetMostVisitedItems(std::move(most_visited_list));
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
// Verify that GetAllItems() returns the correct number of items and the
// code didn't skip a type.
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 8u);
EXPECT_TRUE(HasItemTypes(all_items, {
BirchItemType::kWeather,
BirchItemType::kReleaseNotes,
BirchItemType::kCalendar,
BirchItemType::kAttachment,
BirchItemType::kFile,
BirchItemType::kTab,
BirchItemType::kLastActive,
BirchItemType::kMostVisited,
}));
}
TEST_F(BirchModelTest, SetItemListRecordsHistogram) {
base::HistogramTester histograms;
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
// Simulate a data fetch.
model->RequestBirchDataFetch(/*is_post_login=*/false,
base::BindOnce(&TestModelConsumer::OnItemsReady,
base::Unretained(&consumer),
/*id=*/"0"));
// Insert one item of each type.
model->SetCalendarItems(MakeCalendarItemList(/*event_count=*/1));
model->SetAttachmentItems(MakeAttachmentItemList(/*item_count=*/1));
model->SetRecentTabItems(MakeTabItemList(/*count=*/1));
std::vector<BirchLastActiveItem> last_active_list;
last_active_list.emplace_back(u"active", GURL("https://yahoo.com/"),
base::Time());
model->SetLastActiveItems(std::move(last_active_list));
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
std::vector<BirchWeatherItem> weather_item_list;
weather_item_list.emplace_back(u"cloudy", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_item_list));
std::vector<BirchReleaseNotesItem> release_notes_item_list;
release_notes_item_list.emplace_back(
u"note", u"explore", GURL("https://www.example.com/"), base::Time());
model->SetReleaseNotesItems(std::move(release_notes_item_list));
// Histograms were recorded for each type.
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.Calendar", 1, 1);
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.Attachment", 1, 1);
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.File", 1, 1);
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.Tab", 1, 1);
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.LastActive", 1, 1);
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.Weather", 1, 1);
histograms.ExpectBucketCount("Ash.Birch.ResultsReturned.ReleaseNotes", 1, 1);
}
TEST_F(BirchModelTest, GetItemsForDisplay_EnoughTypes) {
BirchModel* model = Shell::Get()->birch_model();
// Insert two calendar items.
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/2);
// The first event has ranking, the second one has no ranking.
calendar_item_list.front().set_ranking(4.f);
model->SetCalendarItems(std::move(calendar_item_list));
// Insert one item for other types.
std::vector<BirchAttachmentItem> attachment_item_list =
MakeAttachmentItemList(/*item_count=*/1);
attachment_item_list.back().set_ranking(3.f);
model->SetAttachmentItems(std::move(attachment_item_list));
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab", GURL("https://www.example.com/"),
base::Time(), GURL("favicon"), "session",
BirchTabItem::DeviceFormFactor::kDesktop);
tab_item_list.back().set_ranking(2.f);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<BirchFileItem> file_item_list =
MakeFileItemList(/*item_count=*/1);
file_item_list.back().set_ranking(1.f);
model->SetFileSuggestItems(std::move(file_item_list));
std::vector<std::unique_ptr<BirchItem>> items = model->GetItemsForDisplay();
// We should only get 4 ranked items.
ASSERT_EQ(items.size(), 4u);
// The items are in priority order.
EXPECT_FLOAT_EQ(items[0]->ranking(), 1.f);
EXPECT_EQ(items[0]->GetType(), BirchItemType::kFile);
EXPECT_FLOAT_EQ(items[1]->ranking(), 2.f);
EXPECT_EQ(items[1]->GetType(), BirchItemType::kTab);
EXPECT_FLOAT_EQ(items[2]->ranking(), 3.f);
EXPECT_EQ(items[2]->GetType(), BirchItemType::kAttachment);
EXPECT_FLOAT_EQ(items[3]->ranking(), 4.f);
EXPECT_EQ(items[3]->GetType(), BirchItemType::kCalendar);
}
TEST_F(BirchModelTest, GetItemsForDisplay_IncludesDuplicateTypes) {
BirchModel* model = Shell::Get()->birch_model();
// Insert 2 calendar events with high priority.
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/2);
calendar_item_list.front().set_ranking(1.f);
calendar_item_list.back().set_ranking(2.f);
model->SetCalendarItems(std::move(calendar_item_list));
// Then insert 3 other items with lower priority.
std::vector<BirchAttachmentItem> attachment_item_list =
MakeAttachmentItemList(/*item_count=*/1);
attachment_item_list.back().set_ranking(3.f);
model->SetAttachmentItems(std::move(attachment_item_list));
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab", GURL("https://www.example.com/"),
base::Time(), GURL("favicon"), "session",
BirchTabItem::DeviceFormFactor::kDesktop);
tab_item_list.back().set_ranking(4.f);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<BirchFileItem> file_item_list =
MakeFileItemList(/*item_count=*/1);
file_item_list.back().set_ranking(5.f);
model->SetFileSuggestItems(std::move(file_item_list));
std::vector<std::unique_ptr<BirchItem>> items = model->GetItemsForDisplay();
// Both calendar events are included.
EXPECT_FLOAT_EQ(items[0]->ranking(), 1.f);
EXPECT_EQ(items[0]->GetType(), BirchItemType::kCalendar);
EXPECT_FLOAT_EQ(items[1]->ranking(), 2.f);
EXPECT_EQ(items[1]->GetType(), BirchItemType::kCalendar);
EXPECT_FLOAT_EQ(items[2]->ranking(), 3.f);
EXPECT_EQ(items[2]->GetType(), BirchItemType::kAttachment);
EXPECT_FLOAT_EQ(items[3]->ranking(), 4.f);
EXPECT_EQ(items[3]->GetType(), BirchItemType::kTab);
EXPECT_FLOAT_EQ(items[4]->ranking(), 5.f);
EXPECT_EQ(items[4]->GetType(), BirchItemType::kFile);
}
TEST_F(BirchModelTest, GetItemsForDisplay_TwoDuplicateTypes) {
BirchModel* model = Shell::Get()->birch_model();
// Insert 2 items of the same type.
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/2);
calendar_item_list.front().set_ranking(1.f);
calendar_item_list.back().set_ranking(2.f);
model->SetCalendarItems(std::move(calendar_item_list));
// Insert 2 more items of a different type.
std::vector<BirchAttachmentItem> attachment_item_list =
MakeAttachmentItemList(/*item_count=*/2);
attachment_item_list.front().set_ranking(3.f);
attachment_item_list.back().set_ranking(4.f);
model->SetAttachmentItems(std::move(attachment_item_list));
std::vector<std::unique_ptr<BirchItem>> items = model->GetItemsForDisplay();
ASSERT_EQ(items.size(), 4u);
EXPECT_FLOAT_EQ(items[0]->ranking(), 1.f);
EXPECT_EQ(items[0]->GetType(), BirchItemType::kCalendar);
EXPECT_FLOAT_EQ(items[1]->ranking(), 2.f);
EXPECT_EQ(items[1]->GetType(), BirchItemType::kCalendar);
EXPECT_FLOAT_EQ(items[2]->ranking(), 3.f);
EXPECT_EQ(items[2]->GetType(), BirchItemType::kAttachment);
EXPECT_FLOAT_EQ(items[3]->ranking(), 4.f);
EXPECT_EQ(items[3]->GetType(), BirchItemType::kAttachment);
}
TEST_F(BirchModelTest, GetItemsForDisplay_NotEnoughItems) {
BirchModel* model = Shell::Get()->birch_model();
// Insert 3 items of the same type.
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/3);
calendar_item_list[0].set_ranking(1.f);
calendar_item_list[1].set_ranking(2.f);
calendar_item_list[2].set_ranking(3.f);
model->SetCalendarItems(std::move(calendar_item_list));
std::vector<std::unique_ptr<BirchItem>> items = model->GetItemsForDisplay();
// 3 items are returned.
ASSERT_EQ(items.size(), 3u);
EXPECT_FLOAT_EQ(items[0]->ranking(), 1.f);
EXPECT_EQ(items[0]->GetType(), BirchItemType::kCalendar);
EXPECT_FLOAT_EQ(items[1]->ranking(), 2.f);
EXPECT_EQ(items[1]->GetType(), BirchItemType::kCalendar);
EXPECT_FLOAT_EQ(items[2]->ranking(), 3.f);
EXPECT_EQ(items[2]->GetType(), BirchItemType::kCalendar);
}
TEST_F(BirchModelTest, GetItemsForDisplay_NotRankedItem) {
BirchModel* model = Shell::Get()->birch_model();
// Insert 1 regular item and 1 item with no ranking.
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/2);
calendar_item_list.front().set_ranking(1.f);
model->SetCalendarItems(std::move(calendar_item_list));
std::vector<std::unique_ptr<BirchItem>> items = model->GetItemsForDisplay();
// Only 1 item is returned because the unranked item is discarded.
ASSERT_EQ(items.size(), 1u);
EXPECT_FLOAT_EQ(items[0]->ranking(), 1.f);
EXPECT_EQ(items[0]->GetType(), BirchItemType::kCalendar);
}
TEST_F(BirchModelTest, GetItemsForDisplay_NoTitle) {
BirchModel* model = Shell::Get()->birch_model();
// Add an item with an empty title.
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"", GURL("https://www.example.com/"),
base::Time(), GURL("favicon"), "session",
BirchTabItem::DeviceFormFactor::kDesktop);
tab_item_list.back().set_ranking(1.f);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<std::unique_ptr<BirchItem>> items = model->GetItemsForDisplay();
// No items are returned because the item with the empty title was removed.
EXPECT_TRUE(items.empty());
}
TEST_F(BirchModelTest, ModelClearedOnMultiProfileUserSwitch) {
BirchModel* model = Shell::Get()->birch_model();
TestModelConsumer consumer;
// Add an item to the model.
model->SetFileSuggestItems(MakeFileItemList(/*item_count=*/1));
// Set the other types as empty so the model has fresh data.
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetMostVisitedItems({});
model->SetSelfShareItems({});
model->SetLostMediaItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
ASSERT_TRUE(model->IsDataFresh());
// Sign in to a secondary user.
SimulateUserLogin("[email protected]");
ASSERT_FALSE(Shell::Get()->session_controller()->IsUserPrimary());
// The model is empty.
EXPECT_TRUE(model->GetAllItems().empty());
// The data is not fresh.
EXPECT_FALSE(model->IsDataFresh());
}
TEST_F(BirchModelTest, WeatherItemsClearedWhenGeolocationDisabled) {
BirchModel* model = Shell::Get()->birch_model();
// Geolocation starts as allowed.
auto* geolocation_provider = SimpleGeolocationProvider::GetInstance();
ASSERT_EQ(geolocation_provider->GetGeolocationAccessLevel(),
GeolocationAccessLevel::kAllowed);
// Add a weather item.
std::vector<BirchWeatherItem> weather_items;
weather_items.emplace_back(u"Sunny", 70.f, GURL("http://icon.com/"));
model->SetWeatherItems(std::move(weather_items));
ASSERT_FALSE(model->GetWeatherForTest().empty());
// Disable geolocation permission.
geolocation_provider->SetGeolocationAccessLevel(
GeolocationAccessLevel::kDisallowed);
// The weather item is removed.
EXPECT_TRUE(model->GetWeatherForTest().empty());
}
TEST_F(BirchModelTest, RemoveAndFilterTabItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetCalendarItems({});
model->SetAttachmentItems({});
model->SetLastActiveItems({});
model->SetSelfShareItems({});
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
BirchTabItem item0(u"item0", GURL("https://example.com/01"), base::Time(),
GURL(), "", BirchTabItem::DeviceFormFactor::kDesktop);
BirchTabItem item1(u"item1", GURL("https://example.com/11"), base::Time(),
GURL(), "", BirchTabItem::DeviceFormFactor::kDesktop);
BirchTabItem item2(u"item2", GURL("https://example.com/21"), base::Time(),
GURL(), "", BirchTabItem::DeviceFormFactor::kDesktop);
std::vector<BirchTabItem> tab_item_list = {item0, item1, item2};
model->SetRecentTabItems(tab_item_list);
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 3u);
// Remove `item1` and check that it is filtered from `all_items`.
model->RemoveItem(&item1);
all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 2u);
}
TEST_F(BirchModelTest, RemoveAndFilterCalendarItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetSelfShareItems({});
model->SetAttachmentItems({});
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/3);
model->SetCalendarItems(calendar_item_list);
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 3u);
// Remove the second item and check that it is filtered from `all_items`.
model->RemoveItem(&calendar_item_list[1]);
all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 2u);
}
TEST_F(BirchModelTest, RemoveAndFilterAttachmentItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetCalendarItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetSelfShareItems({});
model->SetFileSuggestItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
std::vector<BirchAttachmentItem> attachment_item_list =
MakeAttachmentItemList(/*item_count=*/3);
model->SetAttachmentItems(attachment_item_list);
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 3u);
// Remove the second item and check that it is filtered from `all_items`.
model->RemoveItem(&attachment_item_list[1]);
all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 2u);
}
TEST_F(BirchModelTest, RemoveAndFilterFileItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetCalendarItems({});
model->SetSelfShareItems({});
model->SetAttachmentItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
std::vector<BirchFileItem> file_item_list =
MakeFileItemList(/*item_count=*/3);
model->SetFileSuggestItems(file_item_list);
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 3u);
// Remove the second item and check that it is filtered from `all_items`.
model->RemoveItem(&file_item_list[1]);
all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 2u);
}
TEST_F(BirchModelTest, RemoveFileItemNotifiesBirchClient) {
BirchModel* model = Shell::Get()->birch_model();
std::vector<BirchFileItem> file_item_list;
file_item_list.emplace_back(base::FilePath("/test/path"), "title",
u"suggestion", base::Time(), "file_id_0",
"icon_url");
model->SetFileSuggestItems(file_item_list);
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
// Remove the item.
model->RemoveItem(&file_item_list[0]);
// Verify the birch client was notified of the removal.
EXPECT_EQ(stub_birch_client_.last_removed_path(),
base::FilePath("/test/path"));
}
TEST_F(BirchModelTest, DuplicateFileAndAttachmentItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetCalendarItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetSelfShareItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
std::vector<BirchAttachmentItem> attachment_item_list;
attachment_item_list.emplace_back(
u"Ongoing Event Attachment 1",
/*file_url=*/GURL(),
/*icon_url=*/GURL(),
/*start_time=*/base::Time(TimeFromString("22 Feb 2024 3:00 UTC")),
/*end_time=*/base::Time(TimeFromString("22 Feb 2024 5:00 UTC")),
/*file_id=*/"duplicate_file_id_1");
attachment_item_list.emplace_back(
u"Tomorrow Event Attachment 2",
/*file_url=*/GURL(),
/*icon_url=*/GURL(),
/*start_time=*/base::Time(TimeFromString("23 Feb 2024 3:00 UTC")),
/*end_time=*/base::Time(TimeFromString("23 Feb 2024 5:00 UTC")),
/*file_id=*/"duplicate_file_id_2");
model->SetAttachmentItems(attachment_item_list);
std::vector<BirchFileItem> file_item_list;
file_item_list.emplace_back(
base::FilePath("Recently Edited File 1"), "title_1",
/*justification=*/u"",
/*timestamp=*/base::Time(TimeFromString("22 Feb 2024 3:00 UTC")),
/*file_id=*/"duplicate_file_id_1", "icon_url");
file_item_list.emplace_back(
base::FilePath("Recently Edited File 2"), "recently_edited_title_2",
/*justification=*/u"",
/*timestamp=*/base::Time(TimeFromString("22 Feb 2024 3:00 UTC")),
/*file_id=*/"duplicate_file_id_2", "icon_url");
model->SetFileSuggestItems(file_item_list);
// Calling GetAllItems() should return two items, once attachment and one
// file.
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 2u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kAttachment);
EXPECT_EQ(all_items[0]->title(), u"Ongoing Event Attachment 1");
EXPECT_EQ(all_items[1]->GetType(), BirchItemType::kFile);
EXPECT_EQ(all_items[1]->title(), u"recently_edited_title_2");
}
TEST_F(BirchModelTest, DuplicateSelfShareAndRecentTabItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetCalendarItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetSelfShareItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab", GURL("https://www.example.com/"),
base::Time(), GURL("https://www.favicon.com/"),
"session",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<BirchSelfShareItem> self_share_item_list;
self_share_item_list.emplace_back(
u"self share guid", u"self share tab", GURL("https://www.example.com/"),
base::Time(), u"my device", SecondaryIconType::kTabFromDesktop,
base::DoNothing());
model->SetSelfShareItems(std::move(self_share_item_list));
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kTab);
EXPECT_EQ(all_items[0]->title(), u"tab");
}
TEST_F(BirchModelTest, DuplicateLastActiveAndRecentTabItem) {
BirchModel* model = Shell::Get()->birch_model();
// Set the time to morning.
test_clock_.SetNow(TimeFromString("22 Feb 2024 7:00 UTC"));
// Create a recent tab from more than an hour ago.
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab", GURL("https://www.example.com/"),
test_clock_.Now() - base::Hours(2), GURL(),
"session",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<BirchLastActiveItem> last_active_item_list;
last_active_item_list.emplace_back(
u"last active", GURL("https://www.example.com/"), base::Time());
model->SetLastActiveItems(std::move(last_active_item_list));
// The last active item has the higher priority and hence is shown.
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kLastActive);
EXPECT_EQ(all_items[0]->title(), u"last active");
}
TEST_F(BirchModelTest, DuplicateMostVisitedAndRecentTabItem) {
BirchModel* model = Shell::Get()->birch_model();
// Set the time to morning so that most visited items will be ranked.
test_clock_.SetNow(TimeFromString("22 Feb 2024 7:00 UTC"));
// Create a recent tab from more than an hour ago.
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab", GURL("https://www.example.com/"),
test_clock_.Now() - base::Hours(2), GURL(),
"session",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<BirchMostVisitedItem> most_visited_item_list;
most_visited_item_list.emplace_back(u"most visited",
GURL("https://www.example.com/"));
model->SetMostVisitedItems(std::move(most_visited_item_list));
// The most visited item has the higher priority and hence is shown.
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kMostVisited);
EXPECT_EQ(all_items[0]->title(), u"most visited");
}
TEST_F(BirchModelTest, DifferentSelfShareAndRecentTabItem) {
BirchModel* model = Shell::Get()->birch_model();
model->SetCalendarItems({});
model->SetRecentTabItems({});
model->SetLastActiveItems({});
model->SetSelfShareItems({});
model->SetWeatherItems({});
model->SetReleaseNotesItems({});
std::vector<BirchTabItem> tab_item_list;
tab_item_list.emplace_back(u"tab", GURL("https://www.example.com/"),
base::Time(), GURL("https://www.favicon.com/"),
"session",
BirchTabItem::DeviceFormFactor::kDesktop);
model->SetRecentTabItems(std::move(tab_item_list));
std::vector<BirchSelfShareItem> self_share_item_list;
self_share_item_list.emplace_back(
u"self share guid", u"self share tab",
GURL("https://www.exampletwo.com/"), base::Time(), u"my device",
SecondaryIconType::kTabFromDesktop, base::DoNothing());
model->SetSelfShareItems(std::move(self_share_item_list));
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 2u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kTab);
EXPECT_EQ(all_items[0]->title(), u"tab");
EXPECT_EQ(all_items[1]->GetType(), BirchItemType::kSelfShare);
EXPECT_EQ(all_items[1]->title(), u"self share tab");
}
TEST_F(BirchModelTest, SetClientObservation) {
BirchModel* model = Shell::Get()->birch_model();
TestModelObserver test_observer;
// BirchClient has not been set since observation has started.
EXPECT_FALSE(test_observer.birch_client_set());
// Set the client and expect model observer to be notified.
model->SetClientAndInit(&stub_birch_client_);
EXPECT_TRUE(test_observer.birch_client_set());
}
TEST_F(BirchModelTest, RemoveItemRecordsHistogram) {
base::HistogramTester histograms;
BirchModel* model = Shell::Get()->birch_model();
// Add a calendar item to the model.
std::vector<BirchCalendarItem> calendar_item_list =
MakeCalendarItemList(/*event_count=*/1);
model->SetCalendarItems(calendar_item_list);
// Remove the calendar item, as if a user hid the suggestion chip.
model->RemoveItem(&calendar_item_list[0]);
// Histogram was recorded.
histograms.ExpectBucketCount("Ash.Birch.Chip.Hidden",
BirchItemType::kCalendar, 1);
}
TEST_F(BirchModelTest, RecordProviderHiddenHistograms) {
base::HistogramTester histograms;
// Disable all the prefs, as if the user had hidden each data type.
DisableAllDataTypePrefsExcept(std::vector<const char*>());
// Record histograms.
RecordProviderHiddenHistograms();
// Histograms are recorded. All types are hidden.
histograms.ExpectBucketCount("Ash.Birch.ProviderHidden.Calendar", true, 1);
histograms.ExpectBucketCount("Ash.Birch.ProviderHidden.FileSuggest", true, 1);
histograms.ExpectBucketCount("Ash.Birch.ProviderHidden.ChromeTabs", true, 1);
histograms.ExpectBucketCount("Ash.Birch.ProviderHidden.Weather", true, 1);
histograms.ExpectBucketCount("Ash.Birch.ProviderHidden.ReleaseNotes", true,
1);
}
TEST_F(BirchModelTest, LastActiveItemShownByTime) {
BirchModel* model = Shell::Get()->birch_model();
// Set the time to morning so that last active items will be ranked.
test_clock_.SetNow(TimeFromString("22 Feb 2024 7:00 UTC"));
// Create a last active item.
std::vector<BirchLastActiveItem> last_active_item_list;
last_active_item_list.emplace_back(
u"last active", GURL("https://www.example.com/"), base::Time());
model->SetLastActiveItems(std::move(last_active_item_list));
// The first time we query for items, it is shown.
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kLastActive);
// Advance the time by 1 minute.
test_clock_.Advance(base::Minutes(1));
// The item is still shown.
all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kLastActive);
// Advance the time by 2 minutes (for a total of 3, past the threshold for
// showing most visited items).
test_clock_.Advance(base::Minutes(2));
// The item is not shown.
all_items = model->GetAllItems();
EXPECT_TRUE(all_items.empty());
}
TEST_F(BirchModelTest, MostVisitedItemShownByTime) {
BirchModel* model = Shell::Get()->birch_model();
// Set the time to morning so that most visited items will be ranked.
test_clock_.SetNow(TimeFromString("22 Feb 2024 7:00 UTC"));
// Create a most visited item.
std::vector<BirchMostVisitedItem> most_visited_item_list;
most_visited_item_list.emplace_back(u"most visited",
GURL("https://www.example.com/"));
model->SetMostVisitedItems(std::move(most_visited_item_list));
// The first time we query for items, it is shown.
std::vector<std::unique_ptr<BirchItem>> all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kMostVisited);
// Advance the time by 1 minute.
test_clock_.Advance(base::Minutes(1));
// The item is still shown.
all_items = model->GetAllItems();
ASSERT_EQ(all_items.size(), 1u);
EXPECT_EQ(all_items[0]->GetType(), BirchItemType::kMostVisited);
// Advance the time by 2 minutes (for a total of 3, past the threshold for
// showing most visited items).
test_clock_.Advance(base::Minutes(2));
// The item is not shown.
all_items = model->GetAllItems();
EXPECT_TRUE(all_items.empty());
}
// Tests that the lost media items get refetched when received data provider
// change.
TEST_F(BirchModelTest, UpdateLostMediaItem) {
// Set a lost media data changed callback to birch model.
auto* birch_model = Shell::Get()->birch_model();
birch_model->SetLostMediaDataChangedCallback(
base::DoNothingAs<void(std::unique_ptr<BirchItem>)>());
// Notify lost media data provider changed.
auto& client = stub_birch_client_;
static_cast<StubBirchClient::StubDataProvider*>(client.GetLostMediaProvider())
->RunDataProviderChangedCallback();
// Only lost media items will be requested.
EXPECT_TRUE(client.DidRequestLostMediaDataFetch());
EXPECT_FALSE(client.DidRequestCalendarDataFetch());
EXPECT_FALSE(client.DidRequestFileSuggestDataFetch());
EXPECT_FALSE(client.DidRequestRecentTabsDataFetch());
EXPECT_FALSE(client.DidRequestLastActiveDataFetch());
EXPECT_FALSE(client.DidRequestMostVisitedDataFetch());
EXPECT_FALSE(client.DidRequestSelfShareDataFetch());
EXPECT_FALSE(client.DidRequestReleaseNotesDataFetch());
}
} // namespace ash