// 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/system/focus_mode/focus_mode_controller.h"
#include <memory>
#include "ash/api/tasks/fake_tasks_client.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/ash_prefs.h"
#include "ash/shell.h"
#include "ash/system/focus_mode/focus_mode_controller.h"
#include "ash/system/focus_mode/focus_mode_histogram_names.h"
#include "ash/system/focus_mode/focus_mode_session.h"
#include "ash/system/focus_mode/focus_mode_task_test_utils.h"
#include "ash/system/focus_mode/focus_mode_tray.h"
#include "ash/system/focus_mode/focus_mode_util.h"
#include "ash/system/focus_mode/sounds/focus_mode_sounds_controller.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/system/toast/anchored_nudge.h"
#include "ash/system/toast/anchored_nudge_manager_impl.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/test_ash_web_view_factory.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/values.h"
#include "url/gurl.h"
namespace ash {
namespace {
constexpr char kUser1Email[] = "user1@focusmode";
constexpr char kUser2Email[] = "user2@focusmode";
constexpr base::TimeDelta kShortDuration = base::Minutes(10);
constexpr base::TimeDelta kMediumDuration = base::Minutes(11);
constexpr base::TimeDelta kLongDuration = base::Minutes(30);
bool IsEndingMomentNudgeShown() {
return Shell::Get()->anchored_nudge_manager()->IsNudgeShown(
focus_mode_util::kFocusModeEndingMomentNudgeId);
}
AnchoredNudge* GetShownEndingMomentNudge() {
CHECK(IsEndingMomentNudgeShown());
return Shell::Get()->anchored_nudge_manager()->GetShownNudgeForTest(
focus_mode_util::kFocusModeEndingMomentNudgeId);
}
// Simulate a media with the state of playing or paused based on `is_playing`.
// If `is_focus_mode_media` is true, the media is provided by the focus mode;
// otherwise, it will be a system media outside of the focus mode.
void SimulatePlaybackState(bool is_playing, bool is_focus_mode_media) {
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->state =
media_session::mojom::MediaSessionInfo::SessionState::kActive;
session_info->playback_state =
is_playing ? media_session::mojom::MediaPlaybackState::kPlaying
: media_session::mojom::MediaPlaybackState::kPaused;
if (is_focus_mode_media) {
FocusModeController::Get()
->focus_mode_sounds_controller()
->MediaSessionInfoChanged(std::move(session_info));
} else {
FocusModeController::Get()->SetSystemMediaSessionInfoForTesting(
std::move(session_info));
}
}
// If `focus_gained` is true, the media with `id_observed` will gain the audio
// focus; otherwise, the request id for it will be released.
void SimulateAudioFocusGainedForSelectedPlaylist(
bool focus_gained,
const base::UnguessableToken& id_observed) {
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->state =
media_session::mojom::MediaSessionInfo::SessionState::kActive;
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
media_session::mojom::AudioFocusRequestStatePtr focus(
media_session::mojom::AudioFocusRequestState::New());
auto* controller = FocusModeController::Get();
focus->request_id = id_observed;
focus->session_info = std::move(session_info);
auto* sounds_controller = controller->focus_mode_sounds_controller();
if (focus_gained) {
sounds_controller->OnFocusGained(std::move(focus));
} else {
sounds_controller->OnRequestIdReleased(id_observed);
}
}
// Simulate start playing a newly selected playlist during an active session.
void SimulateStartPlaying() {
auto* controller = FocusModeController::Get();
controller->SetMediaSessionRequestIdForTesting(/*create_media_widget=*/true);
SimulateAudioFocusGainedForSelectedPlaylist(
/*focus_gained=*/true,
/*id_observed=*/controller->GetMediaSessionRequestId());
}
// Simulate stop playing the selected playlist during an active session. Note
// that this function is to stop instead of pause the selected playlist. If you
// want to simulate pause the selected playlist, please call
// `SimulatePlaybackState`.
void SimulateStopPlaying() {
auto* controller = FocusModeController::Get();
SimulateAudioFocusGainedForSelectedPlaylist(
/*focus_gained=*/false,
/*id_observed=*/controller->GetMediaSessionRequestId());
// Update the id for testing once we close the media widget.
controller->SetMediaSessionRequestIdForTesting(/*create_media_widget=*/false);
}
} // namespace
class FocusModeControllerMultiUserTest : public NoSessionAshTestBase {
public:
FocusModeControllerMultiUserTest()
: NoSessionAshTestBase(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
scoped_feature_.InitWithFeatures(
/*enabled_features=*/{features::kFocusMode, features::kFocusModeYTM},
/*disabled_features=*/{});
}
~FocusModeControllerMultiUserTest() override = default;
TestingPrefServiceSimple* user_1_prefs() { return user_1_prefs_; }
TestingPrefServiceSimple* user_2_prefs() { return user_2_prefs_; }
// NoSessionAshTestBase:
void SetUp() override {
CHECK(test_web_view_factory_.get());
NoSessionAshTestBase::SetUp();
TestSessionControllerClient* session_controller =
GetSessionControllerClient();
session_controller->Reset();
// Inject our own PrefServices for each user which enables us to setup the
// Focus Mode restore data before the user signs in.
auto user_1_prefs = std::make_unique<TestingPrefServiceSimple>();
user_1_prefs_ = user_1_prefs.get();
RegisterUserProfilePrefs(user_1_prefs_->registry(), /*country=*/"",
/*for_test=*/true);
auto user_2_prefs = std::make_unique<TestingPrefServiceSimple>();
user_2_prefs_ = user_2_prefs.get();
RegisterUserProfilePrefs(user_2_prefs_->registry(), /*country=*/"",
/*for_test=*/true);
session_controller->AddUserSession(kUser1Email,
user_manager::UserType::kRegular,
/*provide_pref_service=*/false);
session_controller->SetUserPrefService(GetUser1AccountId(),
std::move(user_1_prefs));
session_controller->AddUserSession(kUser2Email,
user_manager::UserType::kRegular,
/*provide_pref_service=*/false);
session_controller->SetUserPrefService(GetUser2AccountId(),
std::move(user_2_prefs));
}
void TearDown() override {
user_1_prefs_ = nullptr;
user_2_prefs_ = nullptr;
NoSessionAshTestBase::TearDown();
}
AccountId GetUser1AccountId() const {
return AccountId::FromUserEmail(kUser1Email);
}
AccountId GetUser2AccountId() const {
return AccountId::FromUserEmail(kUser2Email);
}
void SwitchActiveUser(const AccountId& account_id) {
GetSessionControllerClient()->SwitchActiveUser(account_id);
}
void SimulateUserLogin(const AccountId& account_id) {
SwitchActiveUser(account_id);
GetSessionControllerClient()->SetSessionState(
session_manager::SessionState::ACTIVE);
}
void AdvanceClock(base::TimeDelta time_delta) {
// Note that AdvanceClock() is used here instead of FastForwardBy() to
// prevent long run time during an ash test session.
task_environment()->AdvanceClock(time_delta);
task_environment()->RunUntilIdle();
}
private:
base::test::ScopedFeatureList scoped_feature_;
raw_ptr<TestingPrefServiceSimple> user_1_prefs_ = nullptr;
raw_ptr<TestingPrefServiceSimple> user_2_prefs_ = nullptr;
// Calling the factory constructor is enough to set it up.
std::unique_ptr<TestAshWebViewFactory> test_web_view_factory_ =
std::make_unique<TestAshWebViewFactory>();
};
// Tests that the default Focus Mode prefs are registered, and that they are
// read correctly by `FocusModeController`. Also test that switching users will
// load new user prefs.
TEST_F(FocusModeControllerMultiUserTest, LoadUserPrefsAndSwitchUsers) {
auto& tasks_client1 = CreateFakeTasksClient(GetUser1AccountId());
AddFakeTaskList(tasks_client1, "default");
AddFakeTask(tasks_client1, "default", "task_id_1", "User1 Task");
constexpr base::TimeDelta kDefaultSessionDuration = base::Minutes(25);
constexpr bool kDefaultDNDState = true;
const focus_mode_util::SoundType kUser1SoundType =
focus_mode_util::SoundType::kSoundscape;
constexpr base::TimeDelta kUser2SessionDuration = base::Minutes(200);
constexpr bool kUser2DNDState = false;
const focus_mode_util::SoundType kUser2SoundType =
focus_mode_util::SoundType::kYouTubeMusic;
base::Value::Dict user2_task_dict;
const std::string task_list_id_2 = "task_list_id_2";
const std::string task_id_2 = "task_id_2";
user2_task_dict.Set(focus_mode_util::kTaskListIdKey, task_list_id_2);
user2_task_dict.Set(focus_mode_util::kTaskIdKey, task_id_2);
// Set the secondary user2's Focus Mode prefs.
user_2_prefs()->SetTimeDelta(prefs::kFocusModeSessionDuration,
kUser2SessionDuration);
user_2_prefs()->SetBoolean(prefs::kFocusModeDoNotDisturb, kUser2DNDState);
user_2_prefs()->SetDict(prefs::kFocusModeSelectedTask,
user2_task_dict.Clone());
base::Value::Dict sound_section_dict;
sound_section_dict.Set(focus_mode_util::kSoundTypeKey,
static_cast<int>(kUser2SoundType));
user_2_prefs()->SetDict(prefs::kFocusModeSoundSection,
std::move(sound_section_dict));
// Verify the sound section dictionary values for user2.
sound_section_dict =
user_2_prefs()->GetDict(prefs::kFocusModeSoundSection).Clone();
EXPECT_FALSE(sound_section_dict.empty());
EXPECT_EQ(static_cast<int>(kUser2SoundType),
sound_section_dict.FindInt(focus_mode_util::kSoundTypeKey).value());
// Log in and check to see that the user1 prefs are the default values, since
// there should have been nothing previously.
SimulateUserLogin(GetUser1AccountId());
EXPECT_EQ(kDefaultSessionDuration,
user_1_prefs()->GetTimeDelta(prefs::kFocusModeSessionDuration));
EXPECT_EQ(kDefaultDNDState,
user_1_prefs()->GetBoolean(prefs::kFocusModeDoNotDisturb));
EXPECT_TRUE(user_1_prefs()->GetDict(prefs::kFocusModeSelectedTask).empty());
EXPECT_TRUE(user_1_prefs()->GetDict(prefs::kFocusModeSoundSection).empty());
// Verify that `FocusModeController` has loaded the user prefs.
auto* controller = FocusModeController::Get();
auto* sounds_controller = controller->focus_mode_sounds_controller();
EXPECT_EQ(kDefaultSessionDuration, controller->GetSessionDuration());
EXPECT_EQ(kDefaultDNDState, controller->turn_on_do_not_disturb());
EXPECT_FALSE(controller->HasSelectedTask());
EXPECT_EQ(kUser1SoundType, sounds_controller->sound_type());
// Switch users and verify that `FocusModeController` has loaded the new user
// prefs.
auto& tasks_client2 = CreateFakeTasksClient(GetUser2AccountId());
AddFakeTaskList(tasks_client2, "task_list_id_2");
AddFakeTask(tasks_client2, "task_list_id_2", "task_id_2", "User2 Task");
SwitchActiveUser(GetUser2AccountId());
// Wait for the `UpdateFromUserPrefs()` PostTask to finish.
task_environment()->RunUntilIdle();
EXPECT_EQ(kUser2SessionDuration, controller->GetSessionDuration());
EXPECT_EQ(kUser2DNDState, controller->turn_on_do_not_disturb());
EXPECT_EQ(task_list_id_2,
controller->tasks_model().selected_task()->task_id.list_id);
EXPECT_EQ(task_id_2, controller->tasks_model().selected_task()->task_id.id);
EXPECT_EQ(kUser2SoundType, sounds_controller->sound_type());
}
// Tests that switching users will first clear all cached tasks data, then load
// new user prefs and update the tasks caches.
TEST_F(FocusModeControllerMultiUserTest, SwitchingUsersClearsTasksCache) {
// Setup for user1.
auto& tasks_client1 = CreateFakeTasksClient(GetUser1AccountId());
base::Value::Dict user1_task_dict;
const std::string task_list_id_1 = "task_list_id_1";
const std::string task_id_1 = "task_id_1";
user1_task_dict.Set(focus_mode_util::kTaskListIdKey, task_list_id_1);
user1_task_dict.Set(focus_mode_util::kTaskIdKey, task_id_1);
// Set the primary user1's Focus Mode selected task pref.
user_1_prefs()->SetDict(prefs::kFocusModeSelectedTask,
user1_task_dict.Clone());
AddFakeTaskList(tasks_client1, task_list_id_1);
AddFakeTask(tasks_client1, task_list_id_1, task_id_1, "User1 Task");
// Login as the primary user1.
SimulateUserLogin(GetUser1AccountId());
// Wait for `UpdateFromUserPrefs()` to finish before requesting to update the
// provider cache.
task_environment()->RunUntilIdle();
auto* controller = FocusModeController::Get();
EXPECT_TRUE(controller->HasSelectedTask());
EXPECT_EQ(task_list_id_1,
controller->tasks_model().selected_task()->task_id.list_id);
EXPECT_EQ(task_id_1, controller->tasks_model().selected_task()->task_id.id);
EXPECT_FALSE(controller->tasks_model().tasks().empty());
controller->RequestTasksUpdateForTesting();
EXPECT_TRUE(controller->TasksProviderHasCachedTasksForTesting());
// Setup for user2.
auto& tasks_client2 = CreateFakeTasksClient(GetUser2AccountId());
base::Value::Dict user2_task_dict;
const std::string task_list_id_2 = "task_list_id_2";
const std::string task_id_2 = "task_id_2";
user2_task_dict.Set(focus_mode_util::kTaskListIdKey, task_list_id_2);
user2_task_dict.Set(focus_mode_util::kTaskIdKey, task_id_2);
// Set the secondary user2's Focus Mode selected task pref.
user_2_prefs()->SetDict(prefs::kFocusModeSelectedTask,
user2_task_dict.Clone());
AddFakeTaskList(tasks_client2, task_list_id_2);
AddFakeTask(tasks_client2, task_list_id_2, task_id_2, "User2 Task");
// Switch users and verify that all the tasks caches are cleared.
SwitchActiveUser(GetUser2AccountId());
EXPECT_TRUE(controller->tasks_model().tasks().empty());
EXPECT_FALSE(controller->tasks_model().selected_task());
EXPECT_FALSE(controller->TasksProviderHasCachedTasksForTesting());
// Wait for `UpdateFromUserPrefs()` to finish before requesting to update the
// provider cache.
task_environment()->RunUntilIdle();
EXPECT_EQ(task_list_id_2,
controller->tasks_model().selected_task()->task_id.list_id);
EXPECT_EQ(task_id_2, controller->tasks_model().selected_task()->task_id.id);
EXPECT_FALSE(controller->tasks_model().tasks().empty());
controller->RequestTasksUpdateForTesting();
EXPECT_TRUE(controller->TasksProviderHasCachedTasksForTesting());
}
// Tests that when the user selects a different type of playlist, the user pref
// for the sound section will be updated for this change.
TEST_F(FocusModeControllerMultiUserTest, TogglePlaylistToChangeUserPref) {
SimulateUserLogin(GetUser1AccountId());
const focus_mode_util::SoundType kUser1SoundType =
focus_mode_util::SoundType::kSoundscape;
const focus_mode_util::SoundType kUser1NewSoundType =
focus_mode_util::SoundType::kYouTubeMusic;
// Verify that `FocusModeSoundsController` has loaded the user prefs.
auto* sounds_controller =
FocusModeController::Get()->focus_mode_sounds_controller();
EXPECT_EQ(kUser1SoundType, sounds_controller->sound_type());
focus_mode_util::SelectedPlaylist selected_playlist;
selected_playlist.id = "id0";
selected_playlist.type = focus_mode_util::SoundType::kYouTubeMusic;
selected_playlist.state = focus_mode_util::SoundState::kNone;
// 1. Toggle a YouTube Music type of playlists and verify the pref.
sounds_controller->TogglePlaylist(selected_playlist);
EXPECT_EQ(kUser1NewSoundType, sounds_controller->sound_type());
// The playlist id should be also updated into the user pref.
base::Value::Dict dict =
user_1_prefs()->GetDict(prefs::kFocusModeSoundSection).Clone();
EXPECT_EQ(static_cast<int>(kUser1NewSoundType),
dict.FindInt(focus_mode_util::kSoundTypeKey).value());
EXPECT_EQ(selected_playlist.id,
*(dict.FindString(focus_mode_util::kPlaylistIdKey)));
// 2. Toggle the selected playlist to deselect it and verify the pref.
selected_playlist.state = focus_mode_util::SoundState::kSelected;
sounds_controller->TogglePlaylist(selected_playlist);
dict = user_1_prefs()->GetDict(prefs::kFocusModeSoundSection).Clone();
EXPECT_EQ(static_cast<int>(kUser1NewSoundType),
dict.FindInt(focus_mode_util::kSoundTypeKey).value());
EXPECT_EQ(std::string(), *(dict.FindString(focus_mode_util::kPlaylistIdKey)));
}
TEST_F(FocusModeControllerMultiUserTest, ToggleClosesSystemBubble) {
SimulateUserLogin(GetUser1AccountId());
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
// Show the bubble.
auto* system_tray = GetPrimaryUnifiedSystemTray();
system_tray->ShowBubble();
// Toggle focus mode on, and verify that the bubble is closed.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_FALSE(system_tray->IsBubbleShown());
// Show the bubble again.
system_tray->ShowBubble();
// Toggle focus mode off, and verify that this doesn't affect the bubble
// visibility.
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(system_tray->IsBubbleShown());
}
// Tests that we can determine if a focus session has started before.
TEST_F(FocusModeControllerMultiUserTest, FirstTimeUserFlow) {
SimulateUserLogin(GetUser1AccountId());
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->HasStartedSessionBefore());
FocusModeController::Get()->ToggleFocusMode();
EXPECT_TRUE(controller->HasStartedSessionBefore());
}
// Tests adding and completing tasks, and the changes for the user pref.
TEST_F(FocusModeControllerMultiUserTest, TasksFlow) {
SimulateUserLogin(GetUser1AccountId());
const std::string task_list_id = "list1";
const std::string task_id = "task1";
const std::string title = "Focus Task";
auto& tasks_client = CreateFakeTasksClient(GetUser1AccountId());
tasks_client.set_http_error(google_apis::ApiErrorCode::HTTP_SUCCESS);
AddFakeTaskList(tasks_client, task_list_id);
AddFakeTask(tasks_client, task_list_id, task_id, title);
FocusModeTask task;
task.task_id = {.list_id = task_list_id, .id = task_id};
task.title = title;
task.updated = base::Time::Now();
// Verify that initially there is no selected task.
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->HasSelectedTask());
// Select a task, and verify that the task data is accurate.
controller->tasks_model().SetSelectedTask(task);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
// Verify the selected task info is accurate in the user pref once we start a
// focus session.
base::Value::Dict task_dict =
user_1_prefs()->GetDict(prefs::kFocusModeSelectedTask).Clone();
EXPECT_FALSE(task_dict.empty());
EXPECT_EQ(task_list_id,
*(task_dict.FindString(focus_mode_util::kTaskListIdKey)));
EXPECT_EQ(task_id, *(task_dict.FindString(focus_mode_util::kTaskIdKey)));
// Complete the task during the session, and verify that the task data is
// cleared.
controller->CompleteTask();
EXPECT_FALSE(controller->HasSelectedTask());
task_dict = user_1_prefs()->GetDict(prefs::kFocusModeSelectedTask).Clone();
EXPECT_TRUE(task_dict.empty());
}
// Tests basic ending moment functionality. Includes starting a new session
// after the previous ending moment terminates.
TEST_F(FocusModeControllerMultiUserTest, EndingMoment) {
SimulateUserLogin(GetUser1AccountId());
base::TimeDelta kSessionDuration = base::Minutes(20);
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kSessionDuration);
EXPECT_FALSE(controller->current_session().has_value());
// Toggling focus mode on creates a `current_session`. Verify that we are not
// in the ending moment.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->current_session().has_value());
EXPECT_TRUE(controller->in_focus_session());
// Once the session expires, the ending moment should start.
AdvanceClock(kSessionDuration);
EXPECT_TRUE(controller->in_ending_moment());
// Verifies that the ending moment terminates after the specified duration and
// that there is no longer an existing `current_session`.
AdvanceClock(focus_mode_util::kEndingMomentDuration);
EXPECT_FALSE(controller->current_session().has_value());
// Toggling on a focus session after the previous one expires creates
// a new `current_session`.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->current_session().has_value());
EXPECT_TRUE(controller->in_focus_session());
}
// Tests that we can start a new/separate focus session during an ongoing ending
// moment.
TEST_F(FocusModeControllerMultiUserTest, StartNewSessionDuringEndingMoment) {
SimulateUserLogin(GetUser1AccountId());
base::TimeDelta kSessionDuration = base::Minutes(20);
// Case 1: Normal ending moment timeout.
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kSessionDuration);
EXPECT_FALSE(controller->current_session().has_value());
// Toggle focus mode on, and verify that we are not in the ending moment.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
// Once the session expires, the ending moment should start.
AdvanceClock(kSessionDuration);
EXPECT_TRUE(controller->in_ending_moment());
// Toggling focus mode during the ending moment will start a new session and
// terminate the ending moment.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->current_session().has_value());
EXPECT_TRUE(controller->in_focus_session());
}
// Tests basic ending moment nudge functionality. Includes the nudge appearing
// and disappearing.
TEST_F(FocusModeControllerMultiUserTest, EndingMomentNudgeTest) {
SimulateUserLogin(GetUser1AccountId());
CreateFakeTasksClient(GetUser1AccountId());
base::TimeDelta kSessionDuration = base::Minutes(20);
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kSessionDuration);
auto trigger_ending_moment = [this, controller, kSessionDuration]() {
// Toggling focus mode on and verify that we are not in the ending moment.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
// Once the session expires, the ending moment should start.
AdvanceClock(kSessionDuration);
EXPECT_TRUE(controller->in_ending_moment());
};
// Verify that the nudge is visible when the ending moment is triggered.
trigger_ending_moment();
EXPECT_TRUE(IsEndingMomentNudgeShown());
// After the ending moment nudge shown, we ignore the nudge view for
// accessibility purposes.
EXPECT_EQ(ax::mojom::Role::kNone,
GetShownEndingMomentNudge()->GetAccessibleWindowRole());
// Verify that the ending moment terminating also hides the nudge.
AdvanceClock(focus_mode_util::kEndingMomentDuration);
EXPECT_FALSE(controller->current_session().has_value());
EXPECT_FALSE(IsEndingMomentNudgeShown());
// Trigger the ending moment, then check that the notification disappears when
// we open the tray bubble.
trigger_ending_moment();
EXPECT_TRUE(IsEndingMomentNudgeShown());
FocusModeTray* focus_mode_tray =
StatusAreaWidgetTestHelper::GetStatusAreaWidget()->focus_mode_tray();
LeftClickOn(focus_mode_tray);
EXPECT_TRUE(controller->in_ending_moment());
EXPECT_FALSE(IsEndingMomentNudgeShown());
}
// Verify that when toggling a focus mode, the histogram will record the initial
// session duration.
TEST_F(FocusModeControllerMultiUserTest, CheckInitialDurationHistograms) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
// 1. Start a focus session with the minimum session duration.
controller->SetInactiveSessionDuration(focus_mode_util::kMinimumDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kInitialDurationOnSessionStartsHistogramName,
focus_mode_util::kMinimumDuration.InMinutes(), 1);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
// 2. Start a new focus session with the maximum session duration.
controller->SetInactiveSessionDuration(focus_mode_util::kMaximumDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kInitialDurationOnSessionStartsHistogramName,
focus_mode_util::kMaximumDuration.InMinutes(), 1);
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::kInitialDurationOnSessionStartsHistogramName,
2);
}
// Verify that when we start a focus session, the histogram will record whether
// there is a selected task or not.
TEST_F(FocusModeControllerMultiUserTest, CheckHasSelectedTaskHistogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::kHasSelectedTaskOnSessionStartHistogramName,
0);
// 1. Start a focus session without a selected task.
EXPECT_FALSE(controller->HasSelectedTask());
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kHasSelectedTaskOnSessionStartHistogramName,
false, 1);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
// 2. Start a focus session with a selected task.
FocusModeTask task;
task.task_id = {.list_id = "abc", .id = "1"};
task.title = "Focus Task";
task.updated = base::Time::Now();
controller->SetSelectedTask(task);
EXPECT_TRUE(controller->HasSelectedTask());
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kHasSelectedTaskOnSessionStartHistogramName,
true, 1);
}
// Tests that the histogram will record how many tasks we selected during a
// focus session.
TEST_F(FocusModeControllerMultiUserTest, CheckTasksSelectedHistogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
// Start a focus session.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_FALSE(controller->HasSelectedTask());
// Select a task during the session.
FocusModeTask task;
task.task_id = {.list_id = "abc", .id = "1"};
task.title = "Focus Task";
task.updated = base::Time::Now();
controller->SetSelectedTask(task);
EXPECT_TRUE(controller->HasSelectedTask());
// Remove the task and select a new one.
controller->SetSelectedTask({});
task.task_id = {.list_id = "abc", .id = "2"};
task.title = "A New Focus Task";
task.updated = base::Time::Now();
controller->SetSelectedTask(task);
// End the focus session and check the count.
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kTasksSelectedHistogramName, /*sample=*/2,
/*expected_count=*/1);
}
// Verify that when a session is over, the histogram will record which type of
// the DND state.
TEST_F(FocusModeControllerMultiUserTest, CheckDNDStateOnFocusEndHistogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
auto* message_center = message_center::MessageCenter::Get();
// 1. FocusModeOn - DND enabled by FocusMode (default behavior).
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(controller->turn_on_do_not_disturb());
EXPECT_FALSE(message_center->IsQuietMode());
// Turning on Focus Mode will also turn on DND by default.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(message_center->IsQuietMode());
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kDNDStateOnFocusEndHistogramName,
/*sample=*/
focus_mode_histogram_names::DNDStateOnFocusEndType::kFocusModeOn,
/*expected_count=*/1);
// 2. AlreadyOn - DND was already on before FocusMode started and was on when
// we finished (with NO user interaction during the session).
message_center->SetQuietMode(true);
EXPECT_TRUE(message_center->IsQuietMode());
// Start and end a Focus Mode session.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(message_center->IsQuietMode());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kDNDStateOnFocusEndHistogramName,
/*sample=*/
focus_mode_histogram_names::DNDStateOnFocusEndType::kAlreadyOn,
/*expected_count=*/1);
// 3. AlreadyOff - DND was off when FocusMode started, and is still off (with
// NO user interactions during the session).
message_center->SetQuietMode(false);
EXPECT_FALSE(message_center->IsQuietMode());
// Set `turn_on_do_not_disturb` to prevent Focus Mode from automatically
// turning on DND when a session starts.
controller->set_turn_on_do_not_disturb(false);
// Start a session and end it.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_FALSE(message_center->IsQuietMode());
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(message_center->IsQuietMode());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kDNDStateOnFocusEndHistogramName,
/*sample=*/
focus_mode_histogram_names::DNDStateOnFocusEndType::kAlreadyOff,
/*expected_count=*/1);
// 4. TurnedOn - The user manually toggled DND during the focus session, and
// the session ends with DND on.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_FALSE(message_center->IsQuietMode());
// Turn on DND during the session.
message_center->SetQuietMode(true);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(message_center->IsQuietMode());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kDNDStateOnFocusEndHistogramName,
/*sample=*/
focus_mode_histogram_names::DNDStateOnFocusEndType::kTurnedOn,
/*expected_count=*/1);
// 5. TurnedOff - The user manually toggled DND during the focus session, and
// the session ends with DND off.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
// Turn off DND during the session.
message_center->SetQuietMode(false);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_FALSE(message_center->IsQuietMode());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kDNDStateOnFocusEndHistogramName,
/*sample=*/
focus_mode_histogram_names::DNDStateOnFocusEndType::kTurnedOff,
/*expected_count=*/1);
}
// Verify that when a session is over, the histogram will record how many
// minutes the user extended during the session.
TEST_F(FocusModeControllerMultiUserTest, CheckTimeAddedOnSessionEndHistogram) {
base::HistogramTester histogram_tester;
// 1. Start a session with 10 minutes and extend the session duration while in
// an active session.
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kShortDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_EQ(kShortDuration, controller->session_duration());
controller->ExtendSessionDuration();
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(/*name=*/"Ash.FocusMode.TimeAdded.Short",
/*sample=*/10, /*expected_count=*/1);
// 2. Start a session with 11 minutes. Even not extending the session will
// still trigger the metric to be recorded.
controller->SetInactiveSessionDuration(kMediumDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_EQ(kMediumDuration, controller->session_duration());
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(/*name=*/"Ash.FocusMode.TimeAdded.Medium",
/*sample=*/0, /*expected_count=*/1);
// 3. Start a session with 30 minutes and extend the session.
controller->SetInactiveSessionDuration(kLongDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_EQ(kLongDuration, controller->session_duration());
controller->ExtendSessionDuration();
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(/*name=*/"Ash.FocusMode.TimeAdded.Long",
/*sample=*/10, /*expected_count=*/1);
}
// Verify that when a session is over, the histogram will record the session
// progress percentage.
TEST_F(FocusModeControllerMultiUserTest, CheckPercentCompletedHistogram) {
base::HistogramTester histogram_tester;
// 1. Start a session with 10 minutes and stop the session when the progress
// is 50%.
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kShortDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_EQ(kShortDuration, controller->session_duration());
AdvanceClock(base::Minutes(5));
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/"Ash.FocusMode.PercentOfSessionCompleted.Short",
/*sample=*/50, /*expected_count=*/1);
// 2. Start a session with 11 minutes and stop the session immediately, which
// should record 0%.
controller->SetInactiveSessionDuration(kMediumDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_EQ(kMediumDuration, controller->session_duration());
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/"Ash.FocusMode.PercentOfSessionCompleted.Medium",
/*sample=*/0, /*expected_count=*/1);
// 3. Start a session with 30 minutes and extend the session two times. After
// the ending moment, the histogram should record 100%.
controller->SetInactiveSessionDuration(kLongDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_EQ(kLongDuration, controller->session_duration());
controller->ExtendSessionDuration();
controller->ExtendSessionDuration();
const auto& current_session = controller->current_session();
EXPECT_TRUE(current_session.has_value());
// Once the session expires, the ending moment should start.
AdvanceClock(current_session->session_duration());
EXPECT_TRUE(controller->in_ending_moment());
// After the ending moment expires, the histogram will record 100%.
AdvanceClock(focus_mode_util::kEndingMomentDuration);
histogram_tester.ExpectBucketCount(
/*name=*/"Ash.FocusMode.PercentOfSessionCompleted.Long",
/*sample=*/100, /*expected_count=*/1);
}
// Verify that the histogram will record the number of tasks completed during an
// active session and the ending moment.
TEST_F(FocusModeControllerMultiUserTest, CheckTasksCompletedHistogram) {
base::HistogramTester histogram_tester;
SimulateUserLogin(GetUser1AccountId());
auto* controller = FocusModeController::Get();
EXPECT_FALSE(controller->in_focus_session());
// 1. Select a new task before a session starts, which will not be recorded
// into the histogram.
auto& tasks_client = CreateFakeTasksClient(GetUser1AccountId());
tasks_client.set_http_error(google_apis::ApiErrorCode::HTTP_SUCCESS);
FocusModeTask task;
task.task_id = {.list_id = "list0", .id = "task0"};
task.title = "Focus Task";
task.updated = base::Time::Now();
AddFakeTaskList(tasks_client, task.task_id.list_id);
AddFakeTask(tasks_client, task.task_id.list_id, task.task_id.id, task.title);
controller->RequestTasksUpdateForTesting();
controller->SetSelectedTask(task);
controller->CompleteTask();
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::kTasksCompletedHistogramName, 0);
// 2. Start a focus session and select a task during the active session.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
task.task_id = {.list_id = "list1", .id = "task1"};
task.title = "A New Focus Task";
task.updated = base::Time::Now();
AddFakeTaskList(tasks_client, task.task_id.list_id);
AddFakeTask(tasks_client, task.task_id.list_id, task.task_id.id, task.title);
controller->SetSelectedTask(task);
EXPECT_TRUE(controller->HasSelectedTask());
// Mark the first task as completed.
controller->CompleteTask();
// Select a new task during this session.
task.task_id = {.list_id = "list2", .id = "task2"};
task.title = "A New Focus Task";
task.updated = base::Time::Now();
AddFakeTaskList(tasks_client, task.task_id.list_id);
AddFakeTask(tasks_client, task.task_id.list_id, task.task_id.id, task.title);
controller->SetSelectedTask(task);
// 3. Mark the second task as completed during the ending moment.
const auto& current_session = controller->current_session();
EXPECT_TRUE(current_session.has_value());
AdvanceClock(current_session->session_duration());
EXPECT_TRUE(controller->in_ending_moment());
controller->CompleteTask();
// Verify the histogram after the ending moment expires.
AdvanceClock(focus_mode_util::kEndingMomentDuration);
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kTasksCompletedHistogramName,
/*sample=*/2, /*expected_count=*/1);
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::kTasksCompletedHistogramName, 1);
}
TEST_F(FocusModeControllerMultiUserTest, CheckSessionDurationHistogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kShortDuration);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
const auto& current_session = controller->current_session();
EXPECT_TRUE(current_session.has_value());
AdvanceClock(base::Minutes(2));
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
focus_mode_histogram_names::kSessionDurationHistogramName, /*sample=*/2,
/*expected_count=*/1);
}
TEST_F(FocusModeControllerMultiUserTest,
CheckEndingMomentBubbleActionHistogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
controller->SetInactiveSessionDuration(kShortDuration);
// 1. Bubble was never opened.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
AdvanceClock(kShortDuration);
EXPECT_TRUE(controller->in_ending_moment());
// After the ending moment expires, the histogram will record 100%.
AdvanceClock(focus_mode_util::kEndingMomentDuration);
EXPECT_FALSE(controller->in_ending_moment());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kEndingMomentBubbleActionHistogram,
/*sample=*/
focus_mode_histogram_names::EndingMomentBubbleClosedReason::kIgnored,
/*expected_count=*/1);
// 2. Bubble was opened and minutes were added to the session
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
AdvanceClock(kShortDuration);
EXPECT_TRUE(controller->in_ending_moment());
// Extend the session during the ending moment.
controller->ExtendSessionDuration();
EXPECT_FALSE(controller->in_ending_moment());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kEndingMomentBubbleActionHistogram,
/*sample=*/
focus_mode_histogram_names::EndingMomentBubbleClosedReason::kExtended,
/*expected_count=*/1);
}
// Verify the histogram will record the different state when a session starts.
TEST_F(FocusModeControllerMultiUserTest, CheckStartedWithTaskHistogram) {
base::HistogramTester histogram_tester;
SimulateUserLogin(GetUser1AccountId());
auto* controller = FocusModeController::Get();
// 1. Start a session without a task selected.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_FALSE(controller->HasSelectedTask());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kStartedWithTaskStatekHistogramName,
/*sample=*/focus_mode_histogram_names::StartedWithTaskState::kNoTask,
/*expected_count=*/1);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
// 2. Start a session with a newly selected task.
auto& tasks_client = CreateFakeTasksClient(GetUser1AccountId());
FocusModeTask task;
task.task_id = {.list_id = "list0", .id = "task0"};
task.title = "Focus Task";
task.updated = base::Time::Now();
AddFakeTaskList(tasks_client, task.task_id.list_id);
AddFakeTask(tasks_client, task.task_id.list_id, task.task_id.id, task.title);
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
EXPECT_TRUE(controller->HasSelectedTask());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kStartedWithTaskStatekHistogramName,
/*sample=*/
focus_mode_histogram_names::StartedWithTaskState::kNewlySelectedTask,
/*expected_count=*/1);
// Mark this selected task as completed, and select a new task during this
// session. Don't finish this selected task until the next session.
task.task_id = {.list_id = "list1", .id = "task1"};
task.title = "A New Focus Task";
task.updated = base::Time::Now();
AddFakeTaskList(tasks_client, task.task_id.list_id);
AddFakeTask(tasks_client, task.task_id.list_id, task.task_id.id, task.title);
controller->SetSelectedTask(task);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
EXPECT_TRUE(controller->HasSelectedTask());
// 3. Start a session with the previously selected task.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kStartedWithTaskStatekHistogramName,
/*sample=*/
focus_mode_histogram_names::StartedWithTaskState::kPreviouslySelectedTask,
/*expected_count=*/1);
}
// Tests that when starting a focus session, it records if there is an existing
// media playing.
TEST_F(FocusModeControllerMultiUserTest, CheckMediaPlayingOnStartsHistogram) {
base::HistogramTester histogram_tester;
// 1. Start a focus session without a system media.
auto* controller = FocusModeController::Get();
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::
kStartedWithExistingMediaPlayingHistogramName,
/*sample=*/false, /*expected_count=*/1);
// End the focus session.
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
// 2. Start a focus session with a system media playing.
SimulatePlaybackState(/*is_playing=*/true, /*is_focus_mode_media=*/false);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::
kStartedWithExistingMediaPlayingHistogramName,
/*sample=*/true, /*expected_count=*/1);
// End the focus session.
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
// 3. Start a focus session with a system media paused.
SimulatePlaybackState(/*is_playing=*/false, /*is_focus_mode_media=*/false);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::
kStartedWithExistingMediaPlayingHistogramName,
/*sample=*/false, /*expected_count=*/2);
}
// Tests that the latency for playing a playlist will be recorded on session
// starts and during an active session.
TEST_F(FocusModeControllerMultiUserTest, CheckPlaylistPlayedLatencyHistograms) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
auto* sounds_controller = controller->focus_mode_sounds_controller();
// 1. Simulate that we have a selected playlist with the soundscape type
// before starting a focus session, the histogram will record the latency.
focus_mode_util::SelectedPlaylist selected_playlist;
selected_playlist.id = "id0";
selected_playlist.type = focus_mode_util::SoundType::kSoundscape;
selected_playlist.state = focus_mode_util::SoundState::kNone;
sounds_controller->TogglePlaylist(selected_playlist);
controller->ToggleFocusMode();
SimulateStartPlaying();
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::kSoundscapeLatencyInMillisecondsHistogramName,
1);
// 2. Simulate that we select another soundscape playlist to play during the
// active session, the histogram will record the latency.
SimulateStopPlaying();
selected_playlist.id = "id1";
sounds_controller->TogglePlaylist(selected_playlist);
SimulateStartPlaying();
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::kSoundscapeLatencyInMillisecondsHistogramName,
2);
// Simulate we stop the selected playlist with the soundscape type.
SimulateStopPlaying();
// 3. Simulate that we select a YouTube Music type of playlist to play during
// the active session.
selected_playlist.id = "id2";
selected_playlist.type = focus_mode_util::SoundType::kYouTubeMusic;
sounds_controller->TogglePlaylist(selected_playlist);
SimulateStartPlaying();
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::
kYouTubeMusicLatencyInMillisecondsHistogramName,
1);
// Simulate we stop the current selected playlist and end the current focus
// session.
SimulateStopPlaying();
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
// 4. Simulate that we select a YouTube Music and then start a new focus
// session.
selected_playlist.id = "id3";
sounds_controller->TogglePlaylist(selected_playlist);
controller->ToggleFocusMode();
SimulateStartPlaying();
histogram_tester.ExpectTotalCount(
focus_mode_histogram_names::
kYouTubeMusicLatencyInMillisecondsHistogramName,
2);
}
TEST_F(FocusModeControllerMultiUserTest,
CheckPlaylistPausedEventDuringSessionHitogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
auto* sounds_controller = controller->focus_mode_sounds_controller();
// 1. Start a focus session and pause the playlist during the session.
int count_pause_event = 0;
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
// Select a playlist and pause it for once.
focus_mode_util::SelectedPlaylist selected_playlist;
selected_playlist.id = "id0";
selected_playlist.type = focus_mode_util::SoundType::kSoundscape;
selected_playlist.state = focus_mode_util::SoundState::kNone;
sounds_controller->TogglePlaylist(selected_playlist);
SimulatePlaybackState(/*is_playing=*/true, /*is_focus_mode_media=*/true);
SimulatePlaybackState(/*is_playing=*/false, /*is_focus_mode_media=*/true);
count_pause_event++;
// Select another playlist and pause it for twice.
selected_playlist.id = "id1";
sounds_controller->TogglePlaylist(selected_playlist);
SimulatePlaybackState(/*is_playing=*/true, /*is_focus_mode_media=*/true);
SimulatePlaybackState(/*is_playing=*/false, /*is_focus_mode_media=*/true);
count_pause_event++;
SimulatePlaybackState(/*is_playing=*/true, /*is_focus_mode_media=*/true);
SimulatePlaybackState(/*is_playing=*/false, /*is_focus_mode_media=*/true);
count_pause_event++;
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kMusicPausedEventsCount,
/*sample=*/count_pause_event, /*expected_count=*/1);
// 2. Start a new focus session, and we don't pause the playlist during the
// session.
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
SimulatePlaybackState(/*is_playing=*/true, /*is_focus_mode_media=*/true);
controller->ToggleFocusMode();
EXPECT_FALSE(controller->in_focus_session());
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kMusicPausedEventsCount,
/*sample=*/0, /*expected_count=*/1);
}
TEST_F(FocusModeControllerMultiUserTest, CheckPlaylistChosenHistogram) {
base::HistogramTester histogram_tester;
auto* controller = FocusModeController::Get();
auto* sounds_controller = controller->focus_mode_sounds_controller();
// Create the playlists for two types.
using playlist_type = FocusModeSoundsController::Playlist;
std::unique_ptr<playlist_type> init_soundscapes[] = {
std::make_unique<playlist_type>("id1", "title1", gfx::ImageSkia()),
std::make_unique<playlist_type>("id2", "title2", gfx::ImageSkia()),
std::make_unique<playlist_type>("id3", "title3", gfx::ImageSkia()),
std::make_unique<playlist_type>("id4", "title4", gfx::ImageSkia())};
std::vector<std::unique_ptr<playlist_type>> soundscape_list{
std::make_move_iterator(std::begin(init_soundscapes)),
std::make_move_iterator(std::end(init_soundscapes))};
sounds_controller->set_soundscape_playlists_for_testing(
std::move(soundscape_list));
ASSERT_EQ(sounds_controller->soundscape_playlists().size(), 4u);
std::unique_ptr<playlist_type> init_youtube_music[] = {
std::make_unique<playlist_type>("id1", "title1", gfx::ImageSkia()),
std::make_unique<playlist_type>("id2", "title2", gfx::ImageSkia()),
std::make_unique<playlist_type>("id3", "title3", gfx::ImageSkia()),
std::make_unique<playlist_type>("id4", "title4", gfx::ImageSkia())};
std::vector<std::unique_ptr<FocusModeSoundsController::Playlist>>
youtube_music_list{
std::make_move_iterator(std::begin(init_youtube_music)),
std::make_move_iterator(std::end(init_youtube_music))};
sounds_controller->set_youtube_music_playlists_for_testing(
std::move(youtube_music_list));
ASSERT_EQ(sounds_controller->youtube_music_playlists().size(), 4u);
controller->ToggleFocusMode();
EXPECT_TRUE(controller->in_focus_session());
// Simulate we play a soundscape 2 in session.
focus_mode_util::SelectedPlaylist selected_playlist;
selected_playlist.id = "id2";
selected_playlist.type = focus_mode_util::SoundType::kSoundscape;
selected_playlist.state = focus_mode_util::SoundState::kNone;
sounds_controller->TogglePlaylist(selected_playlist);
SimulateStartPlaying();
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kPlaylistChosenHistogram, /*sample=*/
focus_mode_histogram_names::FocusModePlaylistChosen::kSoundscapes2,
/*expected_count=*/1);
SimulateStopPlaying();
// Simulate we play a YouTube Music 3 in session.
selected_playlist.id = "id3";
selected_playlist.type = focus_mode_util::SoundType::kYouTubeMusic;
sounds_controller->TogglePlaylist(selected_playlist);
SimulateStartPlaying();
histogram_tester.ExpectBucketCount(
/*name=*/focus_mode_histogram_names::kPlaylistChosenHistogram, /*sample=*/
focus_mode_histogram_names::FocusModePlaylistChosen::kYouTubeMusic3,
/*expected_count=*/1);
}
} // namespace ash