| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/ash/birch/birch_keyed_service.h" |
| |
| #include <memory> |
| #include <optional> |
| |
| #include "ash/birch/birch_item.h" |
| #include "ash/birch/birch_model.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/shell.h" |
| #include "ash/system/focus_mode/focus_mode_controller.h" |
| #include "ash/system/focus_mode/focus_mode_util.h" |
| #include "ash/system/focus_mode/sounds/focus_mode_sounds_controller.h" |
| #include "ash/system/video_conference/fake_video_conference_tray_controller.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "base/version_info/version_info.h" |
| #include "chrome/browser/ash/file_suggest/file_suggest_keyed_service.h" |
| #include "chrome/browser/ash/file_suggest/file_suggest_keyed_service_factory.h" |
| #include "chrome/browser/ash/file_suggest/file_suggest_test_util.h" |
| #include "chrome/browser/ash/file_suggest/mock_file_suggest_keyed_service.h" |
| #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" |
| #include "chrome/browser/ash/release_notes/release_notes_storage.h" |
| #include "chrome/browser/favicon/favicon_service_factory.h" |
| #include "chrome/browser/global_features.h" |
| #include "chrome/browser/prefs/browser_prefs.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h" |
| #include "chrome/browser/sync/session_sync_service_factory.h" |
| #include "chrome/browser/sync/sync_service_factory.h" |
| #include "chrome/browser/ui/ash/birch/birch_file_suggest_provider.h" |
| #include "chrome/browser/ui/ash/birch/birch_keyed_service_factory.h" |
| #include "chrome/browser/ui/ash/birch/birch_lost_media_provider.h" |
| #include "chrome/browser/ui/ash/birch/birch_self_share_provider.h" |
| #include "chrome/browser/ui/ash/holding_space/scoped_test_mount_point.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/browser_with_test_window_test.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "chromeos/crosapi/mojom/video_conference.mojom.h" |
| #include "components/favicon/core/test/mock_favicon_service.h" |
| #include "components/send_tab_to_self/send_tab_to_self_entry.h" |
| #include "components/send_tab_to_self/send_tab_to_self_model.h" |
| #include "components/send_tab_to_self/target_device_info.h" |
| #include "components/send_tab_to_self/test_send_tab_to_self_model.h" |
| #include "components/sessions/core/serialized_navigation_entry_test_helper.h" |
| #include "components/signin/public/identity_manager/identity_test_utils.h" |
| #include "components/sync/test/fake_data_type_controller_delegate.h" |
| #include "components/sync/test/test_sync_service.h" |
| #include "components/sync_device_info/device_info_util.h" |
| #include "components/sync_preferences/testing_pref_service_syncable.h" |
| #include "components/sync_sessions/open_tabs_ui_delegate.h" |
| #include "components/sync_sessions/session_sync_service.h" |
| #include "components/sync_sessions/synced_session.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "services/media_session/public/cpp/test/test_media_controller.h" |
| #include "services/media_session/public/mojom/media_session.mojom-shared.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kSessionName1[] = "test_session_name 1"; |
| constexpr char kSessionName2[] = "test_session_name 2"; |
| |
| constexpr char kSessionTag1[] = "SessionTag1"; |
| constexpr char kSessionTag2[] = "SessionTag2"; |
| |
| constexpr char kExampleURL1[] = "http://www.example.com/1"; |
| constexpr char kExampleURL2[] = "http://www.example.com/2"; |
| |
| constexpr char16_t kTabTitle1[] = u"Tab Title 1"; |
| constexpr char16_t kTabTitle2[] = u"Tab Title 2"; |
| |
| constexpr char kTargetDeviceFullName[] = "Device_1"; |
| constexpr char kTargetDeviceShortName[] = "Device_1"; |
| constexpr char kTargetDeviceCacheGuid[] = "device_guid_1"; |
| |
| constexpr char kChromeSyncGuid[] = "Entry Guid"; |
| constexpr char kChromeSyncDeviceName[] = "Device Name"; |
| constexpr char kChromeSyncUrl[] = "https://www.example.com"; |
| |
| constexpr char16_t kSessionMetadataTitle[] = u"Media Title"; |
| constexpr char16_t kSessionMetadataSourceTitle[] = u"youtube.com"; |
| constexpr char16_t kSessionMetadataSourceTitleFull[] = |
| u"https://www.youtube.com"; |
| |
| constexpr char kMediaAppUrl[] = "https://meet.google.com"; |
| constexpr char16_t kMediaAppTitle[] = u"Google Meet"; |
| |
| std::unique_ptr<sync_sessions::SyncedSession> CreateNewSession( |
| const std::string& session_name, |
| const std::string& session_tag, |
| syncer::DeviceInfo::FormFactor form_factor) { |
| auto session = std::make_unique<sync_sessions::SyncedSession>(); |
| auto window = std::make_unique<sync_sessions::SyncedSessionWindow>(); |
| auto tab = std::make_unique<sessions::SessionTab>(); |
| |
| session->SetSessionName(session_name); |
| session->SetDeviceTypeAndFormFactor(sync_pb::SyncEnums::TYPE_UNSET, |
| form_factor); |
| |
| window->wrapped_window.tabs.push_back(std::move(tab)); |
| session->windows[SessionID::NewUnique()] = std::move(window); |
| session->SetSessionTag(session_tag); |
| return session; |
| } |
| |
| class MockSessionSyncService : public sync_sessions::SessionSyncService { |
| public: |
| MockSessionSyncService() = default; |
| ~MockSessionSyncService() override = default; |
| |
| MOCK_METHOD(syncer::GlobalIdMapper*, |
| GetGlobalIdMapper, |
| (), |
| (const, override)); |
| MOCK_METHOD(sync_sessions::OpenTabsUIDelegate*, |
| GetOpenTabsUIDelegate, |
| (), |
| (override)); |
| base::CallbackListSubscription SubscribeToForeignSessionsChanged( |
| const base::RepeatingClosure& cb) override { |
| return subscriber_list_.Add(cb); |
| } |
| MOCK_METHOD(base::WeakPtr<syncer::DataTypeControllerDelegate>, |
| GetControllerDelegate, |
| ()); |
| |
| void NotifyMockForeignSessionsChanged() { subscriber_list_.Notify(); } |
| |
| bool IsSubscribersEmpty() { return subscriber_list_.empty(); } |
| |
| private: |
| base::RepeatingClosureList subscriber_list_; |
| }; |
| |
| class MockOpenTabsUIDelegate : public sync_sessions::OpenTabsUIDelegate { |
| public: |
| MockOpenTabsUIDelegate() { |
| foreign_sessions_owned_.push_back(CreateNewSession( |
| kSessionName1, kSessionTag1, syncer::DeviceInfo::FormFactor::kDesktop)); |
| foreign_sessions_.push_back(foreign_sessions_owned_.back().get()); |
| foreign_sessions_owned_.push_back(CreateNewSession( |
| kSessionName2, kSessionTag2, syncer::DeviceInfo::FormFactor::kPhone)); |
| foreign_sessions_.push_back(foreign_sessions_owned_.back().get()); |
| |
| std::vector<std::unique_ptr<sessions::SessionTab>> session_tabs_one; |
| auto tab = std::make_unique<sessions::SessionTab>(); |
| tab->timestamp = base::Time::Now(); |
| tab->navigations.push_back(sessions::SerializedNavigationEntryTestHelper:: |
| CreateNavigationForTest()); |
| tab->navigations[0].set_timestamp(base::Time::Now()); |
| tab->navigations[0].set_title(kTabTitle1); |
| tab->navigations[0].set_virtual_url(GURL(kExampleURL1)); |
| session_tabs_one.push_back(std::move(tab)); |
| |
| std::vector<std::unique_ptr<sessions::SessionTab>> session_tabs_two; |
| tab = std::make_unique<sessions::SessionTab>(); |
| tab->timestamp = base::Time::Now(); |
| tab->navigations.push_back(sessions::SerializedNavigationEntryTestHelper:: |
| CreateNavigationForTest()); |
| tab->navigations[0].set_timestamp(base::Time::Now() + base::Minutes(5)); |
| tab->navigations[0].set_title(kTabTitle2); |
| tab->navigations[0].set_virtual_url(GURL(kExampleURL2)); |
| session_tabs_two.push_back(std::move(tab)); |
| |
| session_tabs_.emplace(kSessionTag1, std::move(session_tabs_one)); |
| session_tabs_.emplace(kSessionTag2, std::move(session_tabs_two)); |
| } |
| |
| bool GetAllForeignSessions( |
| std::vector<raw_ptr<const sync_sessions::SyncedSession, |
| VectorExperimental>>* sessions) override { |
| *sessions = foreign_sessions_; |
| std::ranges::sort(*sessions, std::greater(), |
| [](const sync_sessions::SyncedSession* session) { |
| return session->GetModifiedTime(); |
| }); |
| |
| return !sessions->empty(); |
| } |
| |
| MOCK_METHOD1(GetLocalSession, |
| bool(const sync_sessions::SyncedSession** local_session)); |
| |
| MOCK_METHOD3(GetForeignTab, |
| bool(const std::string& tag, |
| const SessionID tab_id, |
| const sessions::SessionTab** tab)); |
| |
| MOCK_METHOD1(DeleteForeignSession, void(const std::string& tag)); |
| |
| MOCK_METHOD1( |
| GetForeignSession, |
| std::vector<const sessions::SessionWindow*>(const std::string& tag)); |
| |
| bool GetForeignSessionTabs( |
| const std::string& tag, |
| std::vector<const sessions::SessionTab*>* tabs) override { |
| auto it = session_tabs_.find(tag); |
| if (it != session_tabs_.end()) { |
| for (auto& tab : it->second) { |
| tabs->push_back(tab.get()); |
| } |
| } |
| |
| return true; |
| } |
| |
| private: |
| std::vector<std::unique_ptr<sync_sessions::SyncedSession>> |
| foreign_sessions_owned_; |
| std::vector<raw_ptr<const sync_sessions::SyncedSession, VectorExperimental>> |
| foreign_sessions_; |
| std::map<std::string, std::vector<std::unique_ptr<sessions::SessionTab>>> |
| session_tabs_; |
| }; |
| |
| std::unique_ptr<KeyedService> BuildMockSessionSyncService( |
| content::BrowserContext* context) { |
| return std::make_unique<testing::NiceMock<MockSessionSyncService>>(); |
| } |
| |
| std::unique_ptr<KeyedService> BuildTestSyncService( |
| content::BrowserContext* context) { |
| return std::make_unique<syncer::TestSyncService>(); |
| } |
| |
| class SendTabToSelfModelMock : public send_tab_to_self::TestSendTabToSelfModel { |
| public: |
| SendTabToSelfModelMock() = default; |
| |
| ~SendTabToSelfModelMock() override = default; |
| |
| MOCK_METHOD1(DeleteEntry, void(const std::string&)); |
| MOCK_METHOD1(DismissEntry, void(const std::string&)); |
| |
| send_tab_to_self::SendTabToSelfEntry* AddEntry( |
| const GURL& url, |
| const std::string& title, |
| const std::string& target_device_cache_guid) override { |
| auto entry = std::make_unique<send_tab_to_self::SendTabToSelfEntry>( |
| kChromeSyncGuid, url, title, base::Time::Now(), kChromeSyncDeviceName, |
| target_device_cache_guid); |
| |
| auto* result = entry.get(); |
| |
| entries_.emplace(kChromeSyncGuid, std::move(entry)); |
| |
| return result; |
| } |
| |
| const send_tab_to_self::SendTabToSelfEntry* GetEntryByGUID( |
| const std::string& guid) const override { |
| auto it = entries_.find(guid); |
| return it != entries_.end() ? it->second.get() : nullptr; |
| } |
| |
| std::vector<std::string> GetAllGuids() const override { |
| std::vector<std::string> keys; |
| for (const auto& it : entries_) { |
| DCHECK_EQ(it.first, it.second->GetGUID()); |
| keys.push_back(it.first); |
| } |
| return keys; |
| } |
| |
| void MarkEntryOpened(const std::string& guid) override { |
| auto it = entries_.find(guid); |
| if (it != entries_.end()) { |
| if (auto* entry = it->second.get()) { |
| entry->MarkOpened(); |
| } |
| } |
| } |
| |
| std::vector<send_tab_to_self::TargetDeviceInfo> |
| GetTargetDeviceInfoSortedList() override { |
| return devices_; |
| } |
| |
| void AddMockTargetDevice(syncer::DeviceInfo::FormFactor form_factor) { |
| devices_.emplace_back(kTargetDeviceFullName, kTargetDeviceShortName, |
| kTargetDeviceCacheGuid, form_factor, |
| base::Time::Now()); |
| } |
| |
| private: |
| std::map<std::string, std::unique_ptr<send_tab_to_self::SendTabToSelfEntry>> |
| entries_; |
| |
| std::vector<send_tab_to_self::TargetDeviceInfo> devices_; |
| }; |
| |
| class TestSendTabToSelfSyncService |
| : public send_tab_to_self::SendTabToSelfSyncService { |
| public: |
| TestSendTabToSelfSyncService() : fake_delegate_(syncer::SEND_TAB_TO_SELF) {} |
| |
| ~TestSendTabToSelfSyncService() override = default; |
| |
| send_tab_to_self::SendTabToSelfModel* GetSendTabToSelfModel() override { |
| return &model_mock_; |
| } |
| |
| base::WeakPtr<syncer::DataTypeControllerDelegate> GetControllerDelegate() |
| override { |
| return fake_delegate_.GetWeakPtr(); |
| } |
| |
| protected: |
| syncer::FakeDataTypeControllerDelegate fake_delegate_; |
| SendTabToSelfModelMock model_mock_; |
| }; |
| |
| std::unique_ptr<KeyedService> BuildTestSendTabToSelfSyncService( |
| content::BrowserContext* context) { |
| return std::make_unique<TestSendTabToSelfSyncService>(); |
| } |
| |
| class FaviconServiceMock : public favicon::MockFaviconService { |
| public: |
| FaviconServiceMock() { |
| // This default implementation is provided to satisfy both actual |
| // functionality and the ability to set expectations in tests. |
| ON_CALL(*this, |
| GetLargestRawFaviconForPageURL(testing::_, testing::_, testing::_, |
| testing::_, testing::_)) |
| .WillByDefault(testing::Invoke( |
| this, &FaviconServiceMock::DefaultGetLargestRawFaviconForPageURL)); |
| |
| ON_CALL(*this, GetRawFavicon(testing::_, testing::_, testing::_, testing::_, |
| testing::_)) |
| .WillByDefault( |
| testing::Invoke(this, &FaviconServiceMock::DefaultGetRawFavicon)); |
| } |
| ~FaviconServiceMock() override = default; |
| FaviconServiceMock(const FaviconServiceMock&) = delete; |
| FaviconServiceMock& operator=(const FaviconServiceMock&) = delete; |
| |
| private: |
| base::CancelableTaskTracker::TaskId DefaultGetLargestRawFaviconForPageURL( |
| const GURL& page_url, |
| const std::vector<favicon_base::IconTypeSet>& icon_types, |
| int minimum_size_in_pixels, |
| favicon_base::FaviconRawBitmapCallback callback, |
| base::CancelableTaskTracker* tracker) { |
| favicon_base::FaviconRawBitmapResult result; |
| |
| std::move(callback).Run(result); |
| |
| return base::CancelableTaskTracker::kBadTaskId; |
| } |
| |
| base::CancelableTaskTracker::TaskId DefaultGetRawFavicon( |
| const GURL& icon_url, |
| const favicon_base::IconType& icon_type, |
| int desired_size_in_pixels, |
| favicon_base::FaviconRawBitmapCallback callback, |
| base::CancelableTaskTracker* tracker) { |
| favicon_base::FaviconRawBitmapResult result; |
| |
| std::move(callback).Run(result); |
| |
| return base::CancelableTaskTracker::kBadTaskId; |
| } |
| }; |
| |
| std::unique_ptr<KeyedService> BuildFaviconServiceMock( |
| content::BrowserContext* context) { |
| return std::make_unique<FaviconServiceMock>(); |
| } |
| |
| } // namespace |
| |
| // TODO(https://crbug.com/1370774): move `ScopedTestMountPoint` out of holding |
| // space to remove the dependency on holding space code. |
| using ash::holding_space::ScopedTestMountPoint; |
| |
| using media_session::test::TestMediaController; |
| |
| class BirchKeyedServiceTest : public BrowserWithTestWindowTest { |
| public: |
| BirchKeyedServiceTest() |
| : BrowserWithTestWindowTest( |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME), |
| fake_user_manager_(std::make_unique<FakeChromeUserManager>()) { |
| feature_list_.InitWithFeatures( |
| {features::kReleaseNotesNotificationAllChannels, |
| features::kBirchVideoConferenceSuggestions}, |
| {}); |
| } |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| BrowserWithTestWindowTest::SetUp(); |
| |
| file_suggest_service_ = static_cast<MockFileSuggestKeyedService*>( |
| FileSuggestKeyedServiceFactory::GetInstance()->GetService( |
| GetProfile())); |
| |
| mount_point_ = std::make_unique<ScopedTestMountPoint>( |
| "test_mount", storage::kFileSystemTypeLocal, |
| file_manager::VOLUME_TYPE_TESTING); |
| mount_point_->Mount(GetProfile()); |
| |
| birch_keyed_service_ = |
| BirchKeyedServiceFactory::GetInstance()->GetService(GetProfile()); |
| |
| session_sync_service_ = static_cast<MockSessionSyncService*>( |
| SessionSyncServiceFactory::GetInstance()->GetForProfile(GetProfile())); |
| sync_service_ = static_cast<syncer::TestSyncService*>( |
| SyncServiceFactory::GetForProfile(profile())); |
| |
| SetSessionServiceToReturnOpenTabsDelegate(true); |
| |
| send_tab_to_self_model_ = static_cast<SendTabToSelfModelMock*>( |
| SendTabToSelfSyncServiceFactory::GetForProfile(GetProfile()) |
| ->GetSendTabToSelfModel()); |
| |
| favicon_service_ = |
| static_cast<FaviconServiceMock*>(FaviconServiceFactory::GetForProfile( |
| GetProfile(), ServiceAccessType::EXPLICIT_ACCESS)); |
| |
| // Inject the test media controller into the media controls view. |
| media_controller_ = std::make_unique<TestMediaController>(); |
| |
| GetLostMediaProvider()->set_fake_media_controller_for_testing( |
| media_controller_->CreateMediaControllerRemote()); |
| |
| vc_controller_ = std::make_unique<FakeVideoConferenceTrayController>(); |
| |
| GetLostMediaProvider()->set_fake_video_conference_controller_for_testing( |
| vc_controller_.get()); |
| } |
| |
| void SetSessionServiceToReturnOpenTabsDelegate(bool return_delegate) { |
| EXPECT_CALL(*session_sync_service_, GetOpenTabsUIDelegate()) |
| .WillRepeatedly( |
| testing::Return(return_delegate ? &open_tabs_delegate_ : nullptr)); |
| } |
| |
| void TearDown() override { |
| GetLostMediaProvider()->set_fake_video_conference_controller_for_testing( |
| nullptr); |
| vc_controller_.reset(); |
| media_controller_.reset(); |
| send_tab_to_self_model_ = nullptr; |
| mount_point_.reset(); |
| birch_keyed_service_ = nullptr; |
| file_suggest_service_ = nullptr; |
| session_sync_service_ = nullptr; |
| sync_service_ = nullptr; |
| release_notes_storage_ = nullptr; |
| favicon_service_ = nullptr; |
| BrowserWithTestWindowTest::TearDown(); |
| } |
| |
| void LogIn(std::string_view email, const GaiaId& gaia_id) override { |
| // TODO(crbug.com/40286020): merge into BrowserWithTestWindowTest. |
| const AccountId account_id(AccountId::FromUserEmailGaiaId(email, gaia_id)); |
| fake_user_manager_->AddUser(account_id); |
| fake_user_manager_->LoginUser(account_id); |
| } |
| |
| TestingProfile* CreateProfile(const std::string& profile_name) override { |
| auto* testing_profile = profile_manager()->CreateTestingProfile( |
| profile_name, {}, u"user_name", /*avatar_id=*/0, GetTestingFactories()); |
| return testing_profile; |
| } |
| |
| void SetUpReleaseNotesStorage() { |
| release_notes_storage_ = |
| std::make_unique<ReleaseNotesStorage>(GetProfile()); |
| } |
| |
| void MakePrimaryAccountAvailable() { |
| auto* identity_manager = |
| IdentityManagerFactory::GetForProfile(GetProfile()); |
| signin::MakePrimaryAccountAvailable(identity_manager, "user@gmail.com", |
| signin::ConsentLevel::kSignin); |
| } |
| |
| void AddNewChromeSyncEntry() { |
| const GURL kUrl(kChromeSyncUrl); |
| const std::string kTitle("Chrome Sync Title"); |
| const std::string kTargetDeviceSyncCacheGuid(kTargetDeviceCacheGuid); |
| send_tab_to_self_model_->AddEntry(kUrl, kTitle, kTargetDeviceSyncCacheGuid); |
| } |
| |
| void SimulateMediaMetadataInit() { |
| media_session::MediaMetadata metadata; |
| metadata.source_title = kSessionMetadataSourceTitle; |
| metadata.title = kSessionMetadataTitle; |
| |
| GetLostMediaProvider()->MediaSessionMetadataChanged(metadata); |
| } |
| |
| void SimulateMediaMetadataEnd() { |
| media_session::MediaMetadata metadata; |
| GetLostMediaProvider()->MediaSessionMetadataChanged(metadata); |
| } |
| |
| void SimulateMediaSessionInfoChanged(bool is_playing, |
| SecondaryIconType type) { |
| media_session::mojom::MediaSessionInfoPtr info( |
| media_session::mojom::MediaSessionInfo::New()); |
| info->playback_state = |
| is_playing ? media_session::mojom::MediaPlaybackState::kPlaying |
| : media_session::mojom::MediaPlaybackState::kPaused; |
| info->audio_video_states = |
| std::vector<media_session::mojom::MediaAudioVideoState>(); |
| media_session::mojom::MediaAudioVideoState state; |
| switch (type) { |
| case SecondaryIconType::kLostMediaAudio: |
| state = media_session::mojom::MediaAudioVideoState::kAudioOnly; |
| break; |
| case SecondaryIconType::kLostMediaVideo: |
| state = media_session::mojom::MediaAudioVideoState::kVideoOnly; |
| break; |
| case SecondaryIconType::kLostMediaVideoConference: |
| case SecondaryIconType::kTabFromDesktop: |
| case SecondaryIconType::kTabFromTablet: |
| case SecondaryIconType::kTabFromPhone: |
| case SecondaryIconType::kTabFromUnknown: |
| case SecondaryIconType::kSelfShareIcon: |
| case SecondaryIconType::kNoIcon: |
| state = media_session::mojom::MediaAudioVideoState::kDeprecatedUnknown; |
| break; |
| } |
| info->audio_video_states->emplace_back(state); |
| GetLostMediaProvider()->MediaSessionInfoChanged(std::move(info)); |
| } |
| |
| void AddMediaApp() { |
| vc_controller_->AddMediaApp( |
| crosapi::mojom::VideoConferenceMediaAppInfo::New( |
| /*id=*/base::UnguessableToken::Create(), |
| /*last_activity_time=*/base::Time::Now(), |
| /*is_capturing_camera=*/true, /*is_capturing_microphone=*/false, |
| /*is_capturing_screen=*/false, /*title=*/kMediaAppTitle, |
| /*url=*/GURL(kMediaAppUrl))); |
| } |
| |
| void ClearMediaApps() { vc_controller_->ClearMediaApps(); } |
| |
| void ClearReleaseNotesSurfacesTimesLeftToShowPref() { |
| GetProfile()->GetPrefs()->ClearPref( |
| ::prefs::kReleaseNotesSuggestionChipTimesLeftToShow); |
| } |
| |
| void MarkMilestoneUpToDate() { |
| release_notes_storage_->MarkNotificationShown(); |
| } |
| |
| void MarkReleaseNotesSurfacesTimesLeftToShow(int times_left_to_show) { |
| GetProfile()->GetPrefs()->SetInteger( |
| ::prefs::kReleaseNotesSuggestionChipTimesLeftToShow, |
| times_left_to_show); |
| } |
| |
| int GetCurrentMilestone() { |
| return version_info::GetVersion().components()[0]; |
| } |
| |
| BirchLostMediaProvider* GetLostMediaProvider() { |
| return static_cast<BirchLostMediaProvider*>( |
| birch_keyed_service()->GetLostMediaProvider()); |
| } |
| |
| MockFileSuggestKeyedService* file_suggest_service() { |
| return file_suggest_service_; |
| } |
| |
| MockSessionSyncService* session_sync_service() { |
| return session_sync_service_; |
| } |
| |
| SendTabToSelfModelMock* send_tab_to_self_model() { |
| return send_tab_to_self_model_; |
| } |
| |
| FaviconServiceMock* favicon_service() { return favicon_service_; } |
| |
| TestMediaController* media_controller() const { |
| return media_controller_.get(); |
| } |
| |
| FakeVideoConferenceTrayController* vc_controller() const { |
| return vc_controller_.get(); |
| } |
| |
| syncer::TestSyncService* sync_service() { return sync_service_; } |
| |
| BirchKeyedService* birch_keyed_service() { return birch_keyed_service_; } |
| |
| ScopedTestMountPoint* mount_point() { return mount_point_.get(); } |
| |
| TestingProfile::TestingFactories GetTestingFactories() override { |
| return { |
| TestingProfile::TestingFactory{ |
| SyncServiceFactory::GetInstance(), |
| base::BindRepeating(&BuildTestSyncService)}, |
| TestingProfile::TestingFactory{ |
| FileSuggestKeyedServiceFactory::GetInstance(), |
| base::BindRepeating( |
| &MockFileSuggestKeyedService::BuildMockFileSuggestKeyedService, |
| TestingBrowserProcess::GetGlobal() |
| ->GetFeatures() |
| ->application_locale_storage(), |
| temp_dir_.GetPath())}, |
| TestingProfile::TestingFactory{ |
| SessionSyncServiceFactory::GetInstance(), |
| base::BindRepeating(&BuildMockSessionSyncService)}, |
| TestingProfile::TestingFactory{ |
| SendTabToSelfSyncServiceFactory::GetInstance(), |
| base::BindRepeating(&BuildTestSendTabToSelfSyncService)}, |
| TestingProfile::TestingFactory{ |
| FaviconServiceFactory::GetInstance(), |
| base::BindRepeating(&BuildFaviconServiceMock)}, |
| }; |
| } |
| |
| sync_preferences::TestingPrefServiceSyncable* GetDefaultPrefs() const { |
| return profile()->GetTestingPrefService(); |
| } |
| |
| private: |
| user_manager::TypedScopedUserManager<FakeChromeUserManager> |
| fake_user_manager_; |
| |
| base::ScopedTempDir temp_dir_; |
| |
| std::unique_ptr<ScopedTestMountPoint> mount_point_; |
| |
| raw_ptr<MockFileSuggestKeyedService> file_suggest_service_ = nullptr; |
| |
| raw_ptr<BirchKeyedService> birch_keyed_service_ = nullptr; |
| |
| raw_ptr<MockSessionSyncService> session_sync_service_; |
| |
| raw_ptr<syncer::TestSyncService> sync_service_; |
| |
| raw_ptr<SendTabToSelfModelMock> send_tab_to_self_model_; |
| |
| raw_ptr<FaviconServiceMock> favicon_service_; |
| |
| std::unique_ptr<TestMediaController> media_controller_; |
| |
| std::unique_ptr<FakeVideoConferenceTrayController> vc_controller_; |
| |
| MockOpenTabsUIDelegate open_tabs_delegate_; |
| |
| std::unique_ptr<ReleaseNotesStorage> release_notes_storage_; |
| |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| TEST_F(BirchKeyedServiceTest, HasDataProviders) { |
| WaitUntilFileSuggestServiceReady( |
| ash::FileSuggestKeyedServiceFactory::GetInstance()->GetService( |
| GetProfile())); |
| EXPECT_TRUE(birch_keyed_service()->GetCalendarProvider()); |
| EXPECT_TRUE(birch_keyed_service()->GetFileSuggestProvider()); |
| EXPECT_TRUE(birch_keyed_service()->GetRecentTabsProvider()); |
| EXPECT_TRUE(birch_keyed_service()->GetSelfShareProvider()); |
| EXPECT_TRUE(birch_keyed_service()->GetLostMediaProvider()); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, BirchFileSuggestProvider) { |
| WaitUntilFileSuggestServiceReady( |
| ash::FileSuggestKeyedServiceFactory::GetInstance()->GetService( |
| GetProfile())); |
| |
| EXPECT_EQ(Shell::Get()->birch_model()->GetFileSuggestItemsForTest().size(), |
| 0u); |
| |
| const base::FilePath file_path_1 = mount_point()->CreateArbitraryFile(); |
| const base::FilePath file_path_2 = mount_point()->CreateArbitraryFile(); |
| |
| file_suggest_service()->SetSuggestionsForType( |
| FileSuggestionType::kDriveFile, |
| /*suggestions=*/std::vector<FileSuggestData>{ |
| {FileSuggestionType::kDriveFile, file_path_1, |
| /*title=*/std::nullopt, |
| /*new_prediction_reason=*/std::nullopt, |
| /*modified_time=*/std::nullopt, |
| /*viewed_time=*/std::nullopt, |
| /*shared_time=*/std::nullopt, |
| /*new_score=*/std::nullopt, |
| /*drive_file_id=*/std::nullopt, |
| /*icon_url=*/std::nullopt}, |
| {FileSuggestionType::kDriveFile, file_path_2, |
| /*title=*/std::nullopt, |
| /*new_prediction_reason=*/std::nullopt, |
| /*modified_time=*/std::nullopt, |
| /*viewed_time=*/std::nullopt, |
| /*shared_time=*/std::nullopt, |
| /*new_score=*/std::nullopt, |
| /*drive_file_id=*/std::nullopt, |
| /*icon_url=*/std::nullopt}}); |
| |
| birch_keyed_service() |
| ->GetFileSuggestProviderForTest() |
| ->OnFileSuggestionUpdated(FileSuggestionType::kDriveFile); |
| |
| task_environment()->RunUntilIdle(); |
| |
| // Check that the birch model now has two file suggestions. |
| EXPECT_EQ(Shell::Get()->birch_model()->GetFileSuggestItemsForTest().size(), |
| 2u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, BirchFileSuggestProvider_NoFilesAvailable) { |
| WaitUntilFileSuggestServiceReady( |
| ash::FileSuggestKeyedServiceFactory::GetInstance()->GetService( |
| GetProfile())); |
| |
| BirchModel* model = Shell::Get()->birch_model(); |
| model->SetCalendarItems({}); |
| model->SetRecentTabItems({}); |
| model->SetLastActiveItems({}); |
| model->SetMostVisitedItems({}); |
| model->SetSelfShareItems({}); |
| model->SetLostMediaItems({}); |
| model->SetWeatherItems({}); |
| model->SetReleaseNotesItems({}); |
| model->SetAttachmentItems({}); |
| model->SetCoralItems({}); |
| |
| // Trigger a file update, with no available files. |
| birch_keyed_service() |
| ->GetFileSuggestProviderForTest() |
| ->OnFileSuggestionUpdated(FileSuggestionType::kDriveFile); |
| task_environment()->RunUntilIdle(); |
| |
| // Check that model data is not fresh, because no file items have yet |
| // been provided. |
| EXPECT_EQ(model->GetFileSuggestItemsForTest().size(), 0u); |
| EXPECT_FALSE(model->IsDataFresh()); |
| |
| const base::FilePath file_path_1 = mount_point()->CreateArbitraryFile(); |
| |
| // Once file suggest data has been set and updated, the data should be marked |
| // fresh after the file provider has notified of the change. |
| file_suggest_service()->SetSuggestionsForType( |
| FileSuggestionType::kDriveFile, |
| /*suggestions=*/std::vector<FileSuggestData>{ |
| {FileSuggestionType::kDriveFile, file_path_1, |
| /*title=*/std::nullopt, |
| /*new_prediction_reason=*/std::nullopt, |
| /*modified_time=*/std::nullopt, |
| /*viewed_time=*/std::nullopt, |
| /*shared_time=*/std::nullopt, |
| /*new_score=*/std::nullopt, |
| /*drive_file_id=*/std::nullopt, |
| /*icon_url=*/std::nullopt}}); |
| birch_keyed_service() |
| ->GetFileSuggestProviderForTest() |
| ->OnFileSuggestionUpdated(FileSuggestionType::kDriveFile); |
| task_environment()->RunUntilIdle(); |
| EXPECT_EQ(Shell::Get()->birch_model()->GetFileSuggestItemsForTest().size(), |
| 1u); |
| EXPECT_TRUE(model->IsDataFresh()); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, BirchRecentTabProvider) { |
| WaitUntilFileSuggestServiceReady( |
| ash::FileSuggestKeyedServiceFactory::GetInstance()->GetService( |
| GetProfile())); |
| |
| // No tabs data exists before a data fetch has occurred. |
| EXPECT_EQ(Shell::Get()->birch_model()->GetTabsForTest().size(), 0u); |
| |
| // Request birch data fetch, then verify that tabs data is correct. |
| birch_keyed_service()->GetRecentTabsProvider()->RequestBirchDataFetch(); |
| |
| auto& tabs = Shell::Get()->birch_model()->GetTabsForTest(); |
| ASSERT_EQ(tabs.size(), 2u); |
| |
| EXPECT_EQ(tabs[0].title(), kTabTitle1); |
| EXPECT_EQ(tabs[0].url(), GURL(kExampleURL1)); |
| EXPECT_EQ(tabs[0].session_name(), kSessionName1); |
| EXPECT_EQ(tabs[0].form_factor(), BirchTabItem::DeviceFormFactor::kDesktop); |
| EXPECT_EQ(tabs[0].secondary_icon_type(), SecondaryIconType::kTabFromDesktop); |
| |
| EXPECT_EQ(tabs[1].title(), kTabTitle2); |
| EXPECT_EQ(tabs[1].url(), GURL(kExampleURL2)); |
| EXPECT_EQ(tabs[1].session_name(), kSessionName2); |
| EXPECT_EQ(tabs[1].form_factor(), BirchTabItem::DeviceFormFactor::kPhone); |
| EXPECT_EQ(tabs[1].secondary_icon_type(), SecondaryIconType::kTabFromPhone); |
| |
| // Disable tab sync, then try fetching again and expect an empty list of tabs. |
| sync_service()->GetUserSettings()->SetSelectedTypes( |
| /*sync_everything = */ false, /*types=*/{}); |
| birch_keyed_service()->GetRecentTabsProvider()->RequestBirchDataFetch(); |
| EXPECT_EQ(Shell::Get()->birch_model()->GetTabsForTest().size(), 0u); |
| EXPECT_TRUE(session_sync_service()->IsSubscribersEmpty()); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, ReleaseNotesProvider) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* release_notes_provider = |
| birch_keyed_service()->GetReleaseNotesProvider(); |
| |
| SetUpReleaseNotesStorage(); |
| MakePrimaryAccountAvailable(); |
| |
| EXPECT_EQ(model->GetReleaseNotesItemsForTest().size(), 0u); |
| |
| release_notes_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| auto& release_notes_items = model->GetReleaseNotesItemsForTest(); |
| |
| ASSERT_EQ(release_notes_items.size(), 1u); |
| EXPECT_EQ(release_notes_items[0].title(), u"Chromebook Updated"); |
| EXPECT_EQ(release_notes_items[0].subtitle(), u"See what's new"); |
| EXPECT_EQ(release_notes_items[0].url(), GURL("chrome://help-app/updates")); |
| EXPECT_EQ(GetProfile()->GetPrefs()->GetInteger( |
| ::prefs::kReleaseNotesSuggestionChipTimesLeftToShow), |
| 3); |
| |
| MarkMilestoneUpToDate(); |
| MarkReleaseNotesSurfacesTimesLeftToShow(1); |
| task_environment()->FastForwardBy(base::Hours(23)); |
| |
| release_notes_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems({}); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| |
| EXPECT_EQ(model->GetReleaseNotesItemsForTest().size(), 1u); |
| EXPECT_EQ(GetProfile()->GetPrefs()->GetInteger( |
| ::prefs::kHelpAppNotificationLastShownMilestone), |
| GetCurrentMilestone()); |
| EXPECT_EQ(GetProfile()->GetPrefs()->GetInteger( |
| ::prefs::kReleaseNotesSuggestionChipTimesLeftToShow), |
| 1); |
| |
| ClearReleaseNotesSurfacesTimesLeftToShowPref(); |
| |
| release_notes_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| |
| EXPECT_EQ(model->GetReleaseNotesItemsForTest().size(), 0u); |
| EXPECT_EQ(GetProfile()->GetPrefs()->GetInteger( |
| ::prefs::kHelpAppNotificationLastShownMilestone), |
| GetCurrentMilestone()); |
| EXPECT_TRUE( |
| GetProfile() |
| ->GetPrefs() |
| ->FindPreference(::prefs::kReleaseNotesSuggestionChipTimesLeftToShow) |
| ->IsDefaultValue()); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, BirchRecentTabsWaitForForeignSessionsChange) { |
| SetSessionServiceToReturnOpenTabsDelegate(false); |
| |
| // Request tab data, and check that no tabs are set when no open tabs delegate |
| // is available. |
| birch_keyed_service()->GetRecentTabsProvider()->RequestBirchDataFetch(); |
| EXPECT_EQ(Shell::Get()->birch_model()->GetTabsForTest().size(), 0u); |
| EXPECT_FALSE(session_sync_service()->IsSubscribersEmpty()); |
| |
| SetSessionServiceToReturnOpenTabsDelegate(true); |
| |
| // Notify session service of foreign session change, and check that tabs have |
| // been set by the recent tabs provider. |
| session_sync_service()->NotifyMockForeignSessionsChanged(); |
| EXPECT_EQ(Shell::Get()->birch_model()->GetTabsForTest().size(), 2u); |
| EXPECT_TRUE(session_sync_service()->IsSubscribersEmpty()); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, SelfShareProvider_FromTablet) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* self_share_provider = |
| birch_keyed_service()->GetSelfShareProvider(); |
| |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| |
| AddNewChromeSyncEntry(); |
| send_tab_to_self_model()->AddMockTargetDevice( |
| syncer::DeviceInfo::FormFactor::kTablet); |
| self_share_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetLostMediaItems(std::vector<BirchLostMediaItem>()); |
| auto& self_share_items = model->GetSelfShareItemsForTest(); |
| EXPECT_EQ(self_share_items.size(), 1u); |
| EXPECT_EQ(self_share_items[0].title(), u"Chrome Sync Title"); |
| EXPECT_EQ(self_share_items[0].secondary_icon_type(), |
| SecondaryIconType::kTabFromTablet); |
| |
| // Mark Self Share Item as opened, the provider should now return zero items. |
| model->GetSelfShareItemsForTest()[0].PerformAction(); |
| self_share_provider->RequestBirchDataFetch(); |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, SelfShareProvider_FromPhone) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* self_share_provider = |
| birch_keyed_service()->GetSelfShareProvider(); |
| |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| |
| AddNewChromeSyncEntry(); |
| send_tab_to_self_model()->AddMockTargetDevice( |
| syncer::DeviceInfo::FormFactor::kPhone); |
| self_share_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetLostMediaItems(std::vector<BirchLostMediaItem>()); |
| auto& self_share_items = model->GetSelfShareItemsForTest(); |
| EXPECT_EQ(self_share_items.size(), 1u); |
| EXPECT_EQ(self_share_items[0].secondary_icon_type(), |
| SecondaryIconType::kTabFromPhone); |
| |
| // Mark Self Share Item as opened, the provider should now return zero items. |
| model->GetSelfShareItemsForTest()[0].PerformAction(); |
| self_share_provider->RequestBirchDataFetch(); |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, SelfShareProvider_FromDesktop) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* self_share_provider = |
| birch_keyed_service()->GetSelfShareProvider(); |
| |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| |
| AddNewChromeSyncEntry(); |
| send_tab_to_self_model()->AddMockTargetDevice( |
| syncer::DeviceInfo::FormFactor::kDesktop); |
| self_share_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetLostMediaItems(std::vector<BirchLostMediaItem>()); |
| auto& self_share_items = model->GetSelfShareItemsForTest(); |
| EXPECT_EQ(self_share_items.size(), 1u); |
| EXPECT_EQ(self_share_items[0].secondary_icon_type(), |
| SecondaryIconType::kTabFromDesktop); |
| |
| // Mark Self Share Item as opened, the provider should now return zero items. |
| model->GetSelfShareItemsForTest()[0].PerformAction(); |
| self_share_provider->RequestBirchDataFetch(); |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, LostMediaProvider_AudioItem) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* lost_media_provider = |
| birch_keyed_service()->GetLostMediaProvider(); |
| ClearMediaApps(); |
| |
| EXPECT_EQ(model->GetLostMediaItemsForTest().size(), 0u); |
| |
| SimulateMediaMetadataInit(); |
| SimulateMediaSessionInfoChanged(/*is_playing=*/true, |
| SecondaryIconType::kLostMediaAudio); |
| lost_media_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| |
| auto& lost_media_items = model->GetLostMediaItemsForTest(); |
| EXPECT_EQ(lost_media_items.size(), 1u); |
| EXPECT_EQ(lost_media_items[0].source_url(), |
| GURL(kSessionMetadataSourceTitleFull)); |
| EXPECT_EQ(lost_media_items[0].title(), kSessionMetadataTitle); |
| EXPECT_EQ(lost_media_items[0].secondary_icon_type(), |
| SecondaryIconType::kLostMediaAudio); |
| |
| // Media item should still show after activation. |
| lost_media_items[0].PerformAction(); |
| lost_media_items = model->GetLostMediaItemsForTest(); |
| lost_media_provider->RequestBirchDataFetch(); |
| EXPECT_EQ(lost_media_items.size(), 1u); |
| EXPECT_EQ(lost_media_items[0].source_url(), |
| GURL(kSessionMetadataSourceTitleFull)); |
| EXPECT_EQ(lost_media_items[0].title(), kSessionMetadataTitle); |
| EXPECT_EQ(lost_media_items[0].secondary_icon_type(), |
| SecondaryIconType::kLostMediaAudio); |
| |
| // There should be no items if metadata does not have a valid `source_url` |
| // or `title`. |
| SimulateMediaMetadataEnd(); |
| lost_media_provider->RequestBirchDataFetch(); |
| lost_media_items = model->GetLostMediaItemsForTest(); |
| ASSERT_EQ(lost_media_items.size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, LostMediaProvider_PausedDoesNotShow) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* lost_media_provider = |
| birch_keyed_service()->GetLostMediaProvider(); |
| ClearMediaApps(); |
| |
| // Simulate a piece of media. |
| SimulateMediaMetadataInit(); |
| |
| // Simulate a paused audio stream. |
| SimulateMediaSessionInfoChanged(/*is_playing=*/false, |
| SecondaryIconType::kLostMediaAudio); |
| lost_media_provider->RequestBirchDataFetch(); |
| auto& lost_media_items = model->GetLostMediaItemsForTest(); |
| |
| // The media item does not appear in the model. |
| EXPECT_EQ(lost_media_items.size(), 0u); |
| |
| // Simulate a playing audio stream. |
| SimulateMediaSessionInfoChanged(/*is_playing=*/true, |
| SecondaryIconType::kLostMediaAudio); |
| lost_media_provider->RequestBirchDataFetch(); |
| lost_media_items = model->GetLostMediaItemsForTest(); |
| |
| // The media item appears in the model. |
| EXPECT_EQ(lost_media_items.size(), 1u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, LostMediaProvider_FocusModeDoesNotShow) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* lost_media_provider = |
| birch_keyed_service()->GetLostMediaProvider(); |
| ClearMediaApps(); |
| |
| // Simulate a playing audio stream. |
| SimulateMediaMetadataInit(); |
| SimulateMediaSessionInfoChanged(/*is_playing=*/true, |
| SecondaryIconType::kLostMediaAudio); |
| |
| // Simulate that the audio is coming from a focus mode playlist. |
| focus_mode_util::SelectedPlaylist playlist; |
| playlist.id = "123"; |
| playlist.state = focus_mode_util::SoundState::kPlaying; |
| FocusModeController::Get() |
| ->focus_mode_sounds_controller() |
| ->set_selected_playlist_for_testing(playlist); |
| |
| // Fetch the birch items. |
| lost_media_provider->RequestBirchDataFetch(); |
| auto& lost_media_items = model->GetLostMediaItemsForTest(); |
| |
| // The lost media item does not appear in the model. |
| EXPECT_EQ(lost_media_items.size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, LostMediaProvider_VideoItem) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* lost_media_provider = |
| birch_keyed_service()->GetLostMediaProvider(); |
| ClearMediaApps(); |
| |
| EXPECT_EQ(model->GetLostMediaItemsForTest().size(), 0u); |
| |
| SimulateMediaMetadataInit(); |
| SimulateMediaSessionInfoChanged(/*is_playing=*/true, |
| SecondaryIconType::kLostMediaVideo); |
| lost_media_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| |
| auto& lost_media_items = model->GetLostMediaItemsForTest(); |
| EXPECT_EQ(lost_media_items.size(), 1u); |
| EXPECT_EQ(lost_media_items[0].source_url(), |
| GURL(kSessionMetadataSourceTitleFull)); |
| EXPECT_EQ(lost_media_items[0].title(), kSessionMetadataTitle); |
| EXPECT_EQ(lost_media_items[0].secondary_icon_type(), |
| SecondaryIconType::kLostMediaVideo); |
| |
| // Media item should still show after activation. |
| lost_media_items[0].PerformAction(); |
| lost_media_items = model->GetLostMediaItemsForTest(); |
| lost_media_provider->RequestBirchDataFetch(); |
| EXPECT_EQ(lost_media_items.size(), 1u); |
| EXPECT_EQ(lost_media_items[0].source_url(), |
| GURL(kSessionMetadataSourceTitleFull)); |
| EXPECT_EQ(lost_media_items[0].title(), kSessionMetadataTitle); |
| EXPECT_EQ(lost_media_items[0].secondary_icon_type(), |
| SecondaryIconType::kLostMediaVideo); |
| |
| // There should be no items if metadata does not have a valid `source_url` |
| // or `title`. |
| SimulateMediaMetadataEnd(); |
| lost_media_provider->RequestBirchDataFetch(); |
| lost_media_items = model->GetLostMediaItemsForTest(); |
| ASSERT_EQ(lost_media_items.size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, LostMediaProvider_VideoConferenceItem) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| BirchDataProvider* lost_media_provider = |
| birch_keyed_service()->GetLostMediaProvider(); |
| ClearMediaApps(); |
| |
| EXPECT_EQ(model->GetLostMediaItemsForTest().size(), 0u); |
| |
| // There should be one video conference item if there is both vc and |
| // media items available. |
| SimulateMediaMetadataInit(); |
| AddMediaApp(); |
| lost_media_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| |
| auto& lost_media_items = model->GetLostMediaItemsForTest(); |
| ASSERT_EQ(lost_media_items.size(), 1u); |
| EXPECT_EQ(lost_media_items[0].source_url(), GURL(kMediaAppUrl)); |
| EXPECT_EQ(lost_media_items[0].title(), kMediaAppTitle); |
| EXPECT_EQ(lost_media_items[0].secondary_icon_type(), |
| SecondaryIconType::kLostMediaVideoConference); |
| |
| // VC item still should show after activation. |
| lost_media_items[0].PerformAction(); |
| lost_media_items = model->GetLostMediaItemsForTest(); |
| lost_media_provider->RequestBirchDataFetch(); |
| model->SetCalendarItems(std::vector<BirchCalendarItem>()); |
| model->SetRecentTabItems(std::vector<BirchTabItem>()); |
| model->SetFileSuggestItems(std::vector<BirchFileItem>()); |
| model->SetReleaseNotesItems(std::vector<BirchReleaseNotesItem>()); |
| model->SetSelfShareItems(std::vector<BirchSelfShareItem>()); |
| ASSERT_EQ(lost_media_items.size(), 1u); |
| EXPECT_EQ(lost_media_items[0].source_url(), GURL(kMediaAppUrl)); |
| EXPECT_EQ(lost_media_items[0].title(), kMediaAppTitle); |
| EXPECT_EQ(lost_media_items[0].secondary_icon_type(), |
| SecondaryIconType::kLostMediaVideoConference); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, NoTabSuggestionsWithDisabledChromeSyncPref) { |
| BirchModel* model = Shell::Get()->birch_model(); |
| |
| // Request birch data fetch, verify that tabs are populated. |
| birch_keyed_service()->GetRecentTabsProvider()->RequestBirchDataFetch(); |
| AddNewChromeSyncEntry(); |
| birch_keyed_service()->GetSelfShareProvider()->RequestBirchDataFetch(); |
| EXPECT_EQ(model->GetTabsForTest().size(), 2u); |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 1u); |
| |
| // Disable ChromeSync integrations by policy, no tabs should be fetched. |
| GetDefaultPrefs()->SetList(prefs::kContextualGoogleIntegrationsConfiguration, |
| {}); |
| birch_keyed_service()->GetRecentTabsProvider()->RequestBirchDataFetch(); |
| birch_keyed_service()->GetSelfShareProvider()->RequestBirchDataFetch(); |
| EXPECT_EQ(model->GetTabsForTest().size(), 0u); |
| EXPECT_EQ(model->GetSelfShareItemsForTest().size(), 0u); |
| } |
| |
| TEST_F(BirchKeyedServiceTest, RemoveFileItemFromLauncher) { |
| WaitUntilFileSuggestServiceReady( |
| ash::FileSuggestKeyedServiceFactory::GetInstance()->GetService( |
| GetProfile())); |
| |
| // Override the default behavior in MockFileSuggestKeyedService, which calls |
| // into the production code and causes failures. |
| ON_CALL(*file_suggest_service(), RemoveSuggestionsAndNotify(testing::_)) |
| .WillByDefault([](const std::vector<base::FilePath>& paths) { |
| // Do nothing. |
| }); |
| |
| base::FilePath test_path( |
| "/media/fuse/drivefs-48de6bc248c2f6d8e809521347ef6190/root/Test " |
| "doc.gdoc"); |
| std::vector<base::FilePath> paths = {test_path}; |
| |
| // Removing a file item via the birch keyed service will call into file |
| // suggest keyed service and remove it. |
| EXPECT_CALL(*file_suggest_service(), RemoveSuggestionsAndNotify(paths)); |
| birch_keyed_service()->RemoveFileItemFromLauncher(test_path); |
| } |
| |
| // Verifies that `GetFaviconImage` for icon urls calls `GetRawFavicon` in |
| // favicon service. |
| TEST_F(BirchKeyedServiceTest, GetFaviconImage_ForIconUrl) { |
| GURL icon_url("http://example.com/favicon.ico"); |
| EXPECT_CALL( |
| *favicon_service(), |
| GetRawFavicon(icon_url, testing::_, testing::_, testing::_, testing::_)); |
| birch_keyed_service()->GetFaviconImage(icon_url, /*is_page_url=*/false, |
| base::DoNothing()); |
| } |
| |
| // Verifies that `GetFaviconImage` for page urls calls |
| // `GetLargestRawFaviconForPageURL` in favicon service. |
| TEST_F(BirchKeyedServiceTest, GetFaviconImage_ForPageUrl) { |
| GURL page_url("http://example.com/"); |
| EXPECT_CALL(*favicon_service(), |
| GetLargestRawFaviconForPageURL(page_url, testing::_, testing::_, |
| testing::_, testing::_)); |
| birch_keyed_service()->GetFaviconImage(page_url, /*is_page_url=*/true, |
| base::DoNothing()); |
| } |
| |
| } // namespace ash |