blob: ec514ac380030d4bf4ccf7ad25498b094862548d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/media/media_notification_controller.h"
#include <memory>
#include "ash/media/media_notification_constants.h"
#include "ash/media/media_notification_item.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/unguessable_token.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "ui/message_center/message_center.h"
namespace ash {
namespace {
media_session::mojom::MediaSessionInfoPtr BuildMediaSessionInfo(
bool is_controllable) {
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->is_controllable = is_controllable;
return session_info;
}
media_session::mojom::AudioFocusRequestStatePtr GetRequestStateWithId(
const base::UnguessableToken& id) {
media_session::mojom::AudioFocusRequestStatePtr session(
media_session::mojom::AudioFocusRequestState::New());
session->request_id = id;
session->session_info = BuildMediaSessionInfo(true);
return session;
}
} // namespace
class MediaNotificationControllerTest : public AshTestBase {
public:
MediaNotificationControllerTest() = default;
~MediaNotificationControllerTest() override = default;
// AshTestBase
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
features::kMediaSessionNotification);
AshTestBase::SetUp();
}
void ExpectNotificationCount(unsigned count) {
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
EXPECT_EQ(count, message_center->GetVisibleNotifications().size());
// Media notifications should never be shown as a popup so we always check
// this is empty.
EXPECT_TRUE(message_center->GetPopupNotifications().empty());
}
media_session::MediaMetadata BuildMediaMetadata() {
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
return metadata;
}
void ExpectHistogramCountRecorded(int count, int size) {
histogram_tester_.ExpectBucketCount(
MediaNotificationController::kCountHistogramName, count, size);
}
void ExpectHistogramSourceRecorded(MediaNotificationItem::Source source) {
histogram_tester_.ExpectUniqueSample(
MediaNotificationItem::kSourceHistogramName,
static_cast<base::HistogramBase::Sample>(source), 1);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
base::HistogramTester histogram_tester_;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationControllerTest);
};
// Test toggling the notification multiple times with the same ID. Since the
// notification is keyed by ID we should only ever show one.
TEST_F(MediaNotificationControllerTest, OnFocusGainedLost_SameId) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
Shell::Get()->media_notification_controller()->OnFocusLost(
GetRequestStateWithId(id));
ExpectNotificationCount(0);
}
// Test toggling the notification multiple times with different IDs. This should
// show one notification per ID.
TEST_F(MediaNotificationControllerTest, OnFocusGainedLost_MultipleIds) {
base::UnguessableToken id1 = base::UnguessableToken::Create();
base::UnguessableToken id2 = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id1));
Shell::Get()
->media_notification_controller()
->GetItem(id1.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id2));
Shell::Get()
->media_notification_controller()
->GetItem(id2.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(2);
ExpectHistogramCountRecorded(2, 1);
Shell::Get()->media_notification_controller()->OnFocusLost(
GetRequestStateWithId(id1));
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
}
// Test that a notification is hidden when it becomes uncontrollable. We still
// keep the MediaNotificationItem around in case it becomes controllable again.
TEST_F(MediaNotificationControllerTest,
OnFocusGained_ControllableBecomesUncontrollable) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionInfoChanged(BuildMediaSessionInfo(false));
ExpectNotificationCount(0);
}
// Test that a notification is shown when it becomes controllable.
TEST_F(MediaNotificationControllerTest,
OnFocusGained_NotControllableBecomesControllable) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
media_session::mojom::AudioFocusRequestStatePtr state =
GetRequestStateWithId(id);
state->session_info->is_controllable = false;
Shell::Get()->media_notification_controller()->OnFocusGained(
std::move(state));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(0);
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionInfoChanged(BuildMediaSessionInfo(true));
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
}
// Test hiding a notification with an invalid ID.
TEST_F(MediaNotificationControllerTest, OnFocusLost_Noop) {
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusLost(
GetRequestStateWithId(base::UnguessableToken::Create()));
ExpectNotificationCount(0);
}
// Test that media notifications have the correct custom view type.
TEST_F(MediaNotificationControllerTest, NotificationHasCustomViewType) {
ExpectNotificationCount(0);
base::UnguessableToken id = base::UnguessableToken::Create();
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
message_center::Notification* notification =
message_center::MessageCenter::Get()->FindVisibleNotificationById(
id.ToString());
EXPECT_TRUE(notification);
EXPECT_EQ(kMediaSessionNotificationCustomViewType,
notification->custom_view_type());
}
// Test that if we recieve a null media session info that we hide the
// notification.
TEST_F(MediaNotificationControllerTest, HandleNullMediaSessionInfo) {
ExpectNotificationCount(0);
base::UnguessableToken id = base::UnguessableToken::Create();
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionInfoChanged(nullptr);
ExpectNotificationCount(0);
}
TEST_F(MediaNotificationControllerTest, MediaMetadata_NoArtist) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(metadata);
ExpectNotificationCount(0);
}
TEST_F(MediaNotificationControllerTest, MediaMetadata_NoTitle) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
media_session::MediaMetadata metadata;
metadata.artist = base::ASCIIToUTF16("artist");
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(metadata);
ExpectNotificationCount(0);
}
TEST_F(MediaNotificationControllerTest, MediaMetadataUpdated_MissingInfo) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramCountRecorded(1, 1);
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(media_session::MediaMetadata());
ExpectNotificationCount(0);
}
TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Unknown) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
Shell::Get()->media_notification_controller()->OnFocusGained(
GetRequestStateWithId(id));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kUnknown);
}
TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Web) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
media_session::mojom::AudioFocusRequestStatePtr request =
GetRequestStateWithId(id);
request->source_name = "web";
Shell::Get()->media_notification_controller()->OnFocusGained(
std::move(request));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kWeb);
}
TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Assistant) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
media_session::mojom::AudioFocusRequestStatePtr request =
GetRequestStateWithId(id);
request->source_name = "assistant";
Shell::Get()->media_notification_controller()->OnFocusGained(
std::move(request));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kAssistant);
}
TEST_F(MediaNotificationControllerTest, RecordHistogramSource_Arc) {
base::UnguessableToken id = base::UnguessableToken::Create();
ExpectNotificationCount(0);
media_session::mojom::AudioFocusRequestStatePtr request =
GetRequestStateWithId(id);
request->source_name = "arc";
Shell::Get()->media_notification_controller()->OnFocusGained(
std::move(request));
Shell::Get()
->media_notification_controller()
->GetItem(id.ToString())
->MediaSessionMetadataChanged(BuildMediaMetadata());
ExpectNotificationCount(1);
ExpectHistogramSourceRecorded(MediaNotificationItem::Source::kArc);
}
} // namespace ash