blob: c513b6ffa58fc4dd48b5b9fac5c8273d93876f5f [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 "components/media_message_center/media_notification_view_impl.h"
#include <memory>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/media_message_center/media_notification_background_impl.h"
#include "components/media_message_center/media_notification_constants.h"
#include "components/media_message_center/media_notification_container.h"
#include "components/media_message_center/media_notification_controller.h"
#include "components/media_message_center/media_session_notification_item.h"
#include "services/media_session/public/cpp/test/test_media_controller.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/events/base_event_utils.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/message_center/views/message_view_factory.h"
#include "ui/message_center/views/notification_control_buttons_view.h"
#include "ui/message_center/views/notification_header_view.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/test/views_test_base.h"
namespace media_message_center {
using media_session::mojom::MediaSessionAction;
using media_session::test::TestMediaController;
using testing::_;
using testing::Expectation;
using testing::Invoke;
namespace {
// The icons size is 24 and INSETS_VECTOR_IMAGE_BUTTON will add padding around
// the image.
const int kMediaButtonIconSize = 24;
// The title artist row should always have the same height.
const int kMediaTitleArtistRowExpectedHeight = 48;
const char kTestDefaultAppName[] = "default app name";
const char kTestAppName[] = "app name";
const gfx::Size kWidgetSize(500, 500);
constexpr int kViewWidth = 400;
constexpr int kViewArtworkWidth = kViewWidth * 0.4;
const gfx::Size kViewSize(kViewWidth, 400);
class MockMediaNotificationController : public MediaNotificationController {
public:
MockMediaNotificationController() = default;
~MockMediaNotificationController() = default;
// MediaNotificationController implementation.
MOCK_METHOD(void, ShowNotification, (const std::string& id));
MOCK_METHOD(void, HideNotification, (const std::string& id));
MOCK_METHOD(void, RemoveItem, (const std::string& id));
scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override {
return nullptr;
}
MOCK_METHOD(void,
LogMediaSessionActionButtonPressed,
(const std::string& id,
media_session::mojom::MediaSessionAction action));
private:
DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationController);
};
class MockMediaNotificationContainer : public MediaNotificationContainer {
public:
MockMediaNotificationContainer() = default;
~MockMediaNotificationContainer() = default;
// MediaNotificationContainer implementation.
MOCK_METHOD1(OnExpanded, void(bool expanded));
MOCK_METHOD1(
OnMediaSessionInfoChanged,
void(const media_session::mojom::MediaSessionInfoPtr& session_info));
MOCK_METHOD1(OnMediaSessionMetadataChanged,
void(const media_session::MediaMetadata& metadata));
MOCK_METHOD1(OnVisibleActionsChanged,
void(const base::flat_set<MediaSessionAction>& actions));
MOCK_METHOD1(OnMediaArtworkChanged, void(const gfx::ImageSkia& image));
MOCK_METHOD2(OnColorsChanged, void(SkColor foreground, SkColor background));
MOCK_METHOD0(OnHeaderClicked, void());
MediaNotificationViewImpl* view() const { return view_; }
void SetView(MediaNotificationViewImpl* view) { view_ = view; }
private:
MediaNotificationViewImpl* view_;
DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationContainer);
};
} // namespace
class MediaNotificationViewImplTest : public views::ViewsTestBase {
public:
MediaNotificationViewImplTest()
: views::ViewsTestBase(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~MediaNotificationViewImplTest() override = default;
void SetUp() override {
views::ViewsTestBase::SetUp();
request_id_ = base::UnguessableToken::Create();
// Create a new MediaNotificationViewImpl whenever the
// MediaSessionNotificationItem says to show the notification.
EXPECT_CALL(controller_, ShowNotification(request_id_.ToString()))
.WillRepeatedly(InvokeWithoutArgs(
this, &MediaNotificationViewImplTest::CreateView));
// Create a widget to show on the screen for testing screen coordinates and
// focus.
widget_ = std::make_unique<views::Widget>();
views::Widget::InitParams params =
CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(kWidgetSize);
widget_->Init(std::move(params));
widget_->Show();
CreateViewFromMediaSessionInfo(
media_session::mojom::MediaSessionInfo::New());
}
void CreateViewFromMediaSessionInfo(
media_session::mojom::MediaSessionInfoPtr session_info) {
session_info->is_controllable = true;
mojo::Remote<media_session::mojom::MediaController> controller;
item_ = std::make_unique<MediaSessionNotificationItem>(
&controller_, request_id_.ToString(), std::string(),
std::move(controller), std::move(session_info));
// Update the metadata.
media_session::MediaMetadata metadata;
metadata.title = u"title";
metadata.artist = u"artist";
item_->MediaSessionMetadataChanged(metadata);
// Inject the test media controller into the item.
media_controller_ = std::make_unique<TestMediaController>();
item_->SetMediaControllerForTesting(
media_controller_->CreateMediaControllerRemote());
}
void TearDown() override {
container_.SetView(nullptr);
widget_.reset();
actions_.clear();
views::ViewsTestBase::TearDown();
}
void EnableAllActions() {
actions_.insert(MediaSessionAction::kPlay);
actions_.insert(MediaSessionAction::kPause);
actions_.insert(MediaSessionAction::kPreviousTrack);
actions_.insert(MediaSessionAction::kNextTrack);
actions_.insert(MediaSessionAction::kSeekBackward);
actions_.insert(MediaSessionAction::kSeekForward);
actions_.insert(MediaSessionAction::kStop);
actions_.insert(MediaSessionAction::kEnterPictureInPicture);
actions_.insert(MediaSessionAction::kExitPictureInPicture);
NotifyUpdatedActions();
}
void EnableAction(MediaSessionAction action) {
actions_.insert(action);
NotifyUpdatedActions();
}
void DisableAction(MediaSessionAction action) {
actions_.erase(action);
NotifyUpdatedActions();
}
MockMediaNotificationContainer& container() { return container_; }
MockMediaNotificationController& controller() { return controller_; }
MediaNotificationViewImpl* view() const { return container_.view(); }
TestMediaController* media_controller() const {
return media_controller_.get();
}
message_center::NotificationHeaderView* GetHeaderRow(
MediaNotificationViewImpl* view) const {
return view->header_row_;
}
message_center::NotificationHeaderView* header_row() const {
return GetHeaderRow(view());
}
const std::u16string& accessible_name() const {
return view()->accessible_name_;
}
views::View* button_row() const { return view()->button_row_; }
const views::View* playback_button_container() const {
return view()->playback_button_container_;
}
views::View* title_artist_row() const { return view()->title_artist_row_; }
views::Label* title_label() const { return view()->title_label_; }
views::Label* artist_label() const { return view()->artist_label_; }
views::Button* GetButtonForAction(MediaSessionAction action) const {
auto buttons = view()->get_buttons_for_testing();
const auto i = std::find_if(
buttons.begin(), buttons.end(), [action](const views::View* v) {
return views::Button::AsButton(v)->tag() == static_cast<int>(action);
});
return (i == buttons.end()) ? nullptr : views::Button::AsButton(*i);
}
bool IsActionButtonVisible(MediaSessionAction action) const {
return GetButtonForAction(action)->GetVisible();
}
MediaSessionNotificationItem* GetItem() const { return item_.get(); }
const gfx::ImageSkia& GetArtworkImage() const {
return static_cast<MediaNotificationBackgroundImpl*>(
view()->GetMediaNotificationBackground())
->artwork_;
}
gfx::ImageSkia GetAppIcon() const {
return header_row()->app_icon_for_testing();
}
bool expand_button_enabled() const {
return header_row()->expand_button()->GetVisible();
}
bool GetActuallyExpanded() const { return view()->GetActuallyExpanded(); }
void SimulateButtonClick(MediaSessionAction action) {
views::Button* button = GetButtonForAction(action);
EXPECT_TRUE(button->GetVisible());
views::test::ButtonTestApi(button).NotifyClick(
ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0));
}
void SimulateHeaderClick() {
views::test::ButtonTestApi(header_row())
.NotifyClick(ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(), 0, 0));
}
void SimulateTab() {
ui::KeyEvent pressed_tab(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
view()->GetFocusManager()->OnKeyEvent(pressed_tab);
}
void ExpectHistogramActionRecorded(MediaSessionAction action) {
histogram_tester_.ExpectUniqueSample(
MediaSessionNotificationItem::kUserActionHistogramName,
static_cast<base::HistogramBase::Sample>(action), 1);
}
void ExpectHistogramArtworkRecorded(bool present, int count) {
histogram_tester_.ExpectBucketCount(
MediaNotificationViewImpl::kArtworkHistogramName,
static_cast<base::HistogramBase::Sample>(present), count);
}
void ExpectHistogramMetadataRecorded(
MediaNotificationViewImpl::Metadata metadata,
int count) {
histogram_tester_.ExpectBucketCount(
MediaNotificationViewImpl::kMetadataHistogramName,
static_cast<base::HistogramBase::Sample>(metadata), count);
}
void AdvanceClockMilliseconds(int milliseconds) {
task_environment()->FastForwardBy(
base::TimeDelta::FromMilliseconds(milliseconds));
}
private:
void NotifyUpdatedActions() {
item_->MediaSessionActionsChanged(
std::vector<MediaSessionAction>(actions_.begin(), actions_.end()));
}
void CreateView() {
// Create a MediaNotificationViewImpl.
auto view = std::make_unique<MediaNotificationViewImpl>(
&container_, item_->GetWeakPtr(),
nullptr /* header_row_controls_view */,
base::ASCIIToUTF16(kTestDefaultAppName), kViewWidth,
/*should_show_icon=*/true);
view->SetSize(kViewSize);
// Display it in |widget_|. Widget now owns |view|.
// And associate it with |container_|.
container_.SetView(widget_->SetContentsView(std::move(view)));
}
base::UnguessableToken request_id_;
base::HistogramTester histogram_tester_;
base::flat_set<MediaSessionAction> actions_;
std::unique_ptr<TestMediaController> media_controller_;
MockMediaNotificationContainer container_;
MockMediaNotificationController controller_;
std::unique_ptr<MediaSessionNotificationItem> item_;
std::unique_ptr<views::Widget> widget_;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationViewImplTest);
};
// TODO(crbug.com/1009287): many of these tests are failing on TSan builds.
#if defined(THREAD_SANITIZER)
#define MAYBE_MediaNotificationViewImplTest \
DISABLED_MediaNotificationViewImplTest
class DISABLED_MediaNotificationViewImplTest
: public MediaNotificationViewImplTest {};
#else
#define MAYBE_MediaNotificationViewImplTest MediaNotificationViewImplTest
#endif
TEST_F(MAYBE_MediaNotificationViewImplTest, ButtonsSanityCheck) {
view()->SetExpanded(true);
EnableAllActions();
EXPECT_TRUE(button_row()->GetVisible());
EXPECT_GT(button_row()->width(), 0);
EXPECT_GT(button_row()->height(), 0);
auto buttons = view()->get_buttons_for_testing();
EXPECT_EQ(6u, buttons.size());
for (auto* button : buttons) {
EXPECT_TRUE(button->GetVisible());
EXPECT_LT(kMediaButtonIconSize, button->width());
EXPECT_LT(kMediaButtonIconSize, button->height());
EXPECT_FALSE(views::Button::AsButton(button)->GetAccessibleName().empty());
}
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kEnterPictureInPicture));
// |kPause| cannot be present if |kPlay| is.
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kExitPictureInPicture));
}
#if defined(OS_WIN)
#define MAYBE_ButtonsFocusCheck DISABLED_ButtonsFocusCheck
#else
#define MAYBE_ButtonsFocusCheck ButtonsFocusCheck
#endif
TEST_F(MAYBE_MediaNotificationViewImplTest, MAYBE_ButtonsFocusCheck) {
// Expand and enable all actions to show all buttons.
view()->SetExpanded(true);
EnableAllActions();
views::FocusManager* focus_manager = view()->GetFocusManager();
{
// Focus the first action button.
auto* button = GetButtonForAction(MediaSessionAction::kPreviousTrack);
focus_manager->SetFocusedView(button);
EXPECT_EQ(button, focus_manager->GetFocusedView());
}
SimulateTab();
EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekBackward),
focus_manager->GetFocusedView());
SimulateTab();
EXPECT_EQ(GetButtonForAction(MediaSessionAction::kPlay),
focus_manager->GetFocusedView());
SimulateTab();
EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekForward),
focus_manager->GetFocusedView());
SimulateTab();
EXPECT_EQ(GetButtonForAction(MediaSessionAction::kNextTrack),
focus_manager->GetFocusedView());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PlayPauseButtonTooltipCheck) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
auto* button = GetButtonForAction(MediaSessionAction::kPlay);
std::u16string tooltip = button->GetTooltipText(gfx::Point());
EXPECT_FALSE(tooltip.empty());
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
session_info->is_controllable = true;
GetItem()->MediaSessionInfoChanged(session_info.Clone());
std::u16string new_tooltip = button->GetTooltipText(gfx::Point());
EXPECT_FALSE(new_tooltip.empty());
EXPECT_NE(tooltip, new_tooltip);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, NextTrackButtonClick) {
EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
_, MediaSessionAction::kNextTrack));
EnableAction(MediaSessionAction::kNextTrack);
EXPECT_EQ(0, media_controller()->next_track_count());
SimulateButtonClick(MediaSessionAction::kNextTrack);
GetItem()->FlushForTesting();
EXPECT_EQ(1, media_controller()->next_track_count());
ExpectHistogramActionRecorded(MediaSessionAction::kNextTrack);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PlayButtonClick) {
EXPECT_CALL(controller(),
LogMediaSessionActionButtonPressed(_, MediaSessionAction::kPlay));
EnableAction(MediaSessionAction::kPlay);
EXPECT_EQ(0, media_controller()->resume_count());
SimulateButtonClick(MediaSessionAction::kPlay);
GetItem()->FlushForTesting();
EXPECT_EQ(1, media_controller()->resume_count());
ExpectHistogramActionRecorded(MediaSessionAction::kPlay);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PauseButtonClick) {
EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
_, MediaSessionAction::kPause));
EnableAction(MediaSessionAction::kPause);
EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
EXPECT_EQ(0, media_controller()->suspend_count());
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
session_info->is_controllable = true;
GetItem()->MediaSessionInfoChanged(session_info.Clone());
SimulateButtonClick(MediaSessionAction::kPause);
GetItem()->FlushForTesting();
EXPECT_EQ(1, media_controller()->suspend_count());
ExpectHistogramActionRecorded(MediaSessionAction::kPause);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PreviousTrackButtonClick) {
EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
_, MediaSessionAction::kPreviousTrack));
EnableAction(MediaSessionAction::kPreviousTrack);
EXPECT_EQ(0, media_controller()->previous_track_count());
SimulateButtonClick(MediaSessionAction::kPreviousTrack);
GetItem()->FlushForTesting();
EXPECT_EQ(1, media_controller()->previous_track_count());
ExpectHistogramActionRecorded(MediaSessionAction::kPreviousTrack);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, SeekBackwardButtonClick) {
EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
_, MediaSessionAction::kSeekBackward));
EnableAction(MediaSessionAction::kSeekBackward);
EXPECT_EQ(0, media_controller()->seek_backward_count());
SimulateButtonClick(MediaSessionAction::kSeekBackward);
GetItem()->FlushForTesting();
EXPECT_EQ(1, media_controller()->seek_backward_count());
ExpectHistogramActionRecorded(MediaSessionAction::kSeekBackward);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, SeekForwardButtonClick) {
EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
_, MediaSessionAction::kSeekForward));
EnableAction(MediaSessionAction::kSeekForward);
EXPECT_EQ(0, media_controller()->seek_forward_count());
SimulateButtonClick(MediaSessionAction::kSeekForward);
GetItem()->FlushForTesting();
EXPECT_EQ(1, media_controller()->seek_forward_count());
ExpectHistogramActionRecorded(MediaSessionAction::kSeekForward);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PlayToggle_FromObserver_Empty) {
EnableAction(MediaSessionAction::kPlay);
{
views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
GetButtonForAction(MediaSessionAction::kPlay));
ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
EXPECT_FALSE(button->GetToggled());
}
view()->UpdateWithMediaSessionInfo(
media_session::mojom::MediaSessionInfo::New());
{
views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
GetButtonForAction(MediaSessionAction::kPlay));
ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
EXPECT_FALSE(button->GetToggled());
}
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
PlayToggle_FromObserver_PlaybackState) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
{
views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
GetButtonForAction(MediaSessionAction::kPlay));
ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
EXPECT_FALSE(button->GetToggled());
}
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
view()->UpdateWithMediaSessionInfo(session_info.Clone());
{
views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
GetButtonForAction(MediaSessionAction::kPause));
ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
EXPECT_TRUE(button->GetToggled());
}
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPaused;
view()->UpdateWithMediaSessionInfo(session_info.Clone());
{
views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
GetButtonForAction(MediaSessionAction::kPlay));
ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
EXPECT_FALSE(button->GetToggled());
}
}
TEST_F(MAYBE_MediaNotificationViewImplTest, MetadataIsDisplayed) {
view()->SetExpanded(true);
EnableAllActions();
EXPECT_TRUE(title_artist_row()->GetVisible());
EXPECT_TRUE(title_label()->GetVisible());
EXPECT_TRUE(artist_label()->GetVisible());
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_EQ(kMediaTitleArtistRowExpectedHeight, title_artist_row()->height());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateMetadata_FromObserver) {
EnableAllActions();
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kTitle,
1);
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kArtist,
1);
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kAlbum,
0);
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kCount,
1);
EXPECT_FALSE(header_row()->summary_text_for_testing()->GetVisible());
media_session::MediaMetadata metadata;
metadata.title = u"title2";
metadata.artist = u"artist2";
metadata.album = u"album";
EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_));
GetItem()->MediaSessionMetadataChanged(metadata);
view()->SetExpanded(true);
EXPECT_TRUE(title_artist_row()->GetVisible());
EXPECT_TRUE(title_label()->GetVisible());
EXPECT_TRUE(artist_label()->GetVisible());
EXPECT_TRUE(header_row()->summary_text_for_testing()->GetVisible());
EXPECT_EQ(metadata.title, title_label()->GetText());
EXPECT_EQ(metadata.artist, artist_label()->GetText());
EXPECT_EQ(metadata.album,
header_row()->summary_text_for_testing()->GetText());
EXPECT_EQ(kMediaTitleArtistRowExpectedHeight, title_artist_row()->height());
EXPECT_EQ(u"title2 - artist2 - album", accessible_name());
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kTitle,
2);
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kArtist,
2);
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kAlbum,
1);
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kCount,
2);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateMetadata_AppName) {
EXPECT_EQ(base::ASCIIToUTF16(kTestDefaultAppName),
header_row()->app_name_for_testing());
{
media_session::MediaMetadata metadata;
metadata.title = u"title";
metadata.artist = u"artist";
metadata.source_title = base::ASCIIToUTF16(kTestAppName);
GetItem()->MediaSessionMetadataChanged(metadata);
}
EXPECT_EQ(base::ASCIIToUTF16(kTestAppName),
header_row()->app_name_for_testing());
{
media_session::MediaMetadata metadata;
metadata.title = u"title";
metadata.artist = u"artist";
GetItem()->MediaSessionMetadataChanged(metadata);
}
EXPECT_EQ(base::ASCIIToUTF16(kTestDefaultAppName),
header_row()->app_name_for_testing());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
MediaSessionAction::kSeekForward,
MediaSessionAction::kEnterPictureInPicture})));
EnableAllActions();
view()->SetExpanded(false);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPlay));
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kSeekBackward,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekForward,
MediaSessionAction::kEnterPictureInPicture})));
DisableAction(MediaSessionAction::kPreviousTrack);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
MediaSessionAction::kSeekForward,
MediaSessionAction::kEnterPictureInPicture})));
EnableAction(MediaSessionAction::kPreviousTrack);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
MediaSessionAction::kEnterPictureInPicture})));
DisableAction(MediaSessionAction::kSeekForward);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
MediaSessionAction::kSeekForward,
MediaSessionAction::kEnterPictureInPicture})));
EnableAction(MediaSessionAction::kSeekForward);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenExpanded) {
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
MediaSessionAction::kSeekForward,
MediaSessionAction::kEnterPictureInPicture})));
EnableAllActions();
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_CALL(
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
MediaSessionAction::kSeekForward,
MediaSessionAction::kEnterPictureInPicture})));
view()->SetExpanded(true);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPlay));
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ClickHeader_ToggleExpand) {
view()->SetExpanded(true);
EnableAllActions();
EXPECT_TRUE(GetActuallyExpanded());
SimulateHeaderClick();
EXPECT_FALSE(GetActuallyExpanded());
SimulateHeaderClick();
EXPECT_TRUE(GetActuallyExpanded());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ActionButtonsHiddenByDefault) {
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPlay));
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ActionButtonsToggleVisibility) {
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
EnableAction(MediaSessionAction::kNextTrack);
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
DisableAction(MediaSessionAction::kNextTrack);
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
}
TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateArtworkFromItem) {
int title_artist_width = title_artist_row()->width();
const SkColor accent = header_row()->accent_color_for_testing().value();
gfx::Size size = view()->size();
EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(2);
EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(2);
SkBitmap image;
image.allocN32Pixels(10, 10);
image.eraseColor(SK_ColorGREEN);
EXPECT_TRUE(GetArtworkImage().isNull());
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, image);
ExpectHistogramArtworkRecorded(true, 1);
// Ensure the title artist row has a small width than before now that we
// have artwork.
EXPECT_GT(title_artist_width, title_artist_row()->width());
// Ensure that the title artist row does not extend into the artwork bounds.
EXPECT_LE(kViewWidth - kViewArtworkWidth, title_artist_row()->width());
// Ensure that the image is displayed in the background artwork and that the
// size of the notification was not affected.
EXPECT_FALSE(GetArtworkImage().isNull());
EXPECT_EQ(gfx::Size(10, 10), GetArtworkImage().size());
EXPECT_EQ(size, view()->size());
auto accent_color = header_row()->accent_color_for_testing();
ASSERT_TRUE(accent_color.has_value());
EXPECT_NE(accent, accent_color.value());
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
ExpectHistogramArtworkRecorded(false, 1);
// Ensure the title artist row goes back to the original width now that we
// do not have any artwork.
EXPECT_EQ(title_artist_width, title_artist_row()->width());
// Ensure that the background artwork was reset and the size was still not
// affected.
EXPECT_TRUE(GetArtworkImage().isNull());
EXPECT_EQ(size, view()->size());
accent_color = header_row()->accent_color_for_testing();
ASSERT_TRUE(accent_color.has_value());
EXPECT_EQ(accent, accent_color.value());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ExpandableDefaultState) {
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
ExpandablePlayPauseActionCountsOnce) {
view()->SetExpanded(true);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
EnableAction(MediaSessionAction::kPreviousTrack);
EnableAction(MediaSessionAction::kNextTrack);
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
view()->UpdateWithMediaSessionInfo(session_info);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
EnableAction(MediaSessionAction::kSeekForward);
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_TRUE(expand_button_enabled());
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
BecomeExpandableAndWasNotExpandable) {
view()->SetExpanded(true);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
EnableAllActions();
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_TRUE(expand_button_enabled());
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
BecomeExpandableButWasAlreadyExpandable) {
view()->SetExpanded(true);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
EnableAllActions();
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_TRUE(expand_button_enabled());
DisableAction(MediaSessionAction::kSeekForward);
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_TRUE(expand_button_enabled());
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
BecomeNotExpandableAndWasExpandable) {
view()->SetExpanded(true);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
EnableAllActions();
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_TRUE(expand_button_enabled());
DisableAction(MediaSessionAction::kPreviousTrack);
DisableAction(MediaSessionAction::kNextTrack);
DisableAction(MediaSessionAction::kSeekBackward);
DisableAction(MediaSessionAction::kSeekForward);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
BecomeNotExpandableButWasAlreadyNotExpandable) {
view()->SetExpanded(true);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
EnableAction(MediaSessionAction::kSeekForward);
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_FALSE(expand_button_enabled());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ActionButtonRowSizeAndAlignment) {
EnableAction(MediaSessionAction::kPlay);
views::Button* button = GetButtonForAction(MediaSessionAction::kPlay);
int button_x = button->GetBoundsInScreen().x();
// When collapsed the button row should be a fixed width.
EXPECT_FALSE(GetActuallyExpanded());
EXPECT_EQ(124, button_row()->width());
EnableAllActions();
view()->SetExpanded(true);
// When expanded the button row should be wider and the play button should
// have shifted to the left.
EXPECT_TRUE(GetActuallyExpanded());
EXPECT_LT(124, button_row()->width());
EXPECT_GT(button_x, button->GetBoundsInScreen().x());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, NotifysContainerOfExpandedState) {
// Track the expanded state given to |container_|.
bool expanded = false;
EXPECT_CALL(container(), OnExpanded(_))
.WillRepeatedly(Invoke([&expanded](bool exp) { expanded = exp; }));
// Expand the view implicitly via |EnableAllActions()|.
view()->SetExpanded(true);
EnableAllActions();
EXPECT_TRUE(expanded);
// Explicitly contract the view.
view()->SetExpanded(false);
EXPECT_FALSE(expanded);
// Explicitly expand the view.
view()->SetExpanded(true);
EXPECT_TRUE(expanded);
// Implicitly contract the view by removing available actions.
DisableAction(MediaSessionAction::kPreviousTrack);
DisableAction(MediaSessionAction::kNextTrack);
DisableAction(MediaSessionAction::kSeekBackward);
DisableAction(MediaSessionAction::kSeekForward);
EXPECT_FALSE(expanded);
}
TEST_F(MAYBE_MediaNotificationViewImplTest, AccessibleNodeData) {
ui::AXNodeData data;
view()->GetAccessibleNodeData(&data);
EXPECT_TRUE(
data.HasStringAttribute(ax::mojom::StringAttribute::kRoleDescription));
EXPECT_EQ(u"title - artist", accessible_name());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateMetadata) {
media_session::MediaMetadata metadata;
metadata.title = u"title2";
metadata.artist = u"artist2";
metadata.album = u"album";
EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_)).Times(0);
GetItem()->Freeze(base::DoNothing());
GetItem()->MediaSessionMetadataChanged(metadata);
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateImage) {
SkBitmap image;
image.allocN32Pixels(10, 10);
image.eraseColor(SK_ColorMAGENTA);
EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(0);
EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(0);
GetItem()->Freeze(base::DoNothing());
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, image);
EXPECT_TRUE(GetArtworkImage().isNull());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdatePlaybackState) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
EXPECT_CALL(container(), OnMediaSessionInfoChanged(_)).Times(0);
GetItem()->Freeze(base::DoNothing());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
GetItem()->MediaSessionInfoChanged(session_info.Clone());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateActions) {
EXPECT_FALSE(
GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
GetItem()->Freeze(base::DoNothing());
EnableAction(MediaSessionAction::kSeekForward);
EXPECT_FALSE(
GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DisableInteraction) {
EnableAllActions();
EXPECT_EQ(0, media_controller()->next_track_count());
GetItem()->Freeze(base::DoNothing());
SimulateButtonClick(MediaSessionAction::kNextTrack);
GetItem()->FlushForTesting();
EXPECT_EQ(0, media_controller()->next_track_count());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingDoesntMissUpdates) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
// Freeze the item and clear the metadata.
base::MockOnceClosure unfrozen_callback;
EXPECT_CALL(unfrozen_callback, Run).Times(0);
GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
// The item should be frozen and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
// Bind the item to a new controller that's playing instead of paused.
auto new_media_controller = std::make_unique<TestMediaController>();
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
session_info->is_controllable = true;
GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
session_info.Clone());
// The item will receive a MediaSessionInfoChanged.
GetItem()->MediaSessionInfoChanged(session_info.Clone());
// The item should still be frozen, and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
// Update the metadata.
EXPECT_CALL(unfrozen_callback, Run);
media_session::MediaMetadata metadata;
metadata.title = u"title2";
metadata.artist = u"artist2";
GetItem()->MediaSessionMetadataChanged(metadata);
// The item should no longer be frozen, and we should see the updated data.
EXPECT_FALSE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title2", title_label()->GetText());
EXPECT_EQ(u"artist2", artist_label()->GetText());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForArtwork_Timeout) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
// Set an image before freezing.
SkBitmap initial_image;
initial_image.allocN32Pixels(10, 10);
initial_image.eraseColor(SK_ColorMAGENTA);
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, initial_image);
EXPECT_FALSE(GetArtworkImage().isNull());
// Freeze the item and clear the metadata.
base::MockOnceClosure unfrozen_callback;
EXPECT_CALL(unfrozen_callback, Run).Times(0);
GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
// The item should be frozen and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
// Bind the item to a new controller that's playing instead of paused.
auto new_media_controller = std::make_unique<TestMediaController>();
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
session_info->is_controllable = true;
GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
session_info.Clone());
// The item will receive a MediaSessionInfoChanged.
GetItem()->MediaSessionInfoChanged(session_info.Clone());
// The item should still be frozen, and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
// Update the metadata.
media_session::MediaMetadata metadata;
metadata.title = u"title2";
metadata.artist = u"artist2";
GetItem()->MediaSessionMetadataChanged(metadata);
// The item should still be frozen, and waiting for a new image.
EXPECT_TRUE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
// Once the freeze timer fires, the item should unfreeze even if there's no
// artwork.
EXPECT_CALL(unfrozen_callback, Run);
AdvanceClockMilliseconds(2600);
EXPECT_FALSE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title2", title_label()->GetText());
EXPECT_EQ(u"artist2", artist_label()->GetText());
EXPECT_TRUE(GetArtworkImage().isNull());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForActions) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
EnableAction(MediaSessionAction::kNextTrack);
EnableAction(MediaSessionAction::kPreviousTrack);
// Freeze the item and clear the metadata and actions.
base::MockOnceClosure unfrozen_callback;
EXPECT_CALL(unfrozen_callback, Run).Times(0);
GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
DisableAction(MediaSessionAction::kPlay);
DisableAction(MediaSessionAction::kPause);
DisableAction(MediaSessionAction::kNextTrack);
DisableAction(MediaSessionAction::kPreviousTrack);
// The item should be frozen and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
// Bind the item to a new controller that's playing instead of paused.
auto new_media_controller = std::make_unique<TestMediaController>();
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
session_info->is_controllable = true;
GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
session_info.Clone());
// The item will receive a MediaSessionInfoChanged.
GetItem()->MediaSessionInfoChanged(session_info.Clone());
// The item should still be frozen, and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
// Update the metadata.
media_session::MediaMetadata metadata;
metadata.title = u"title2";
metadata.artist = u"artist2";
GetItem()->MediaSessionMetadataChanged(metadata);
// The item should still be frozen, and waiting for new actions.
EXPECT_TRUE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
// Once we receive actions, the item should unfreeze.
EXPECT_CALL(unfrozen_callback, Run);
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
EnableAction(MediaSessionAction::kSeekForward);
EnableAction(MediaSessionAction::kSeekBackward);
EXPECT_FALSE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
EXPECT_EQ(u"title2", title_label()->GetText());
EXPECT_EQ(u"artist2", artist_label()->GetText());
}
TEST_F(MAYBE_MediaNotificationViewImplTest,
UnfreezingWaitsForArtwork_ReceiveArtwork) {
EnableAction(MediaSessionAction::kPlay);
EnableAction(MediaSessionAction::kPause);
// Set an image before freezing.
SkBitmap initial_image;
initial_image.allocN32Pixels(10, 10);
initial_image.eraseColor(SK_ColorMAGENTA);
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, initial_image);
EXPECT_FALSE(GetArtworkImage().isNull());
// Freeze the item and clear the metadata.
base::MockOnceClosure unfrozen_callback;
EXPECT_CALL(unfrozen_callback, Run).Times(0);
GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
// The item should be frozen and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
// Bind the item to a new controller that's playing instead of paused.
auto new_media_controller = std::make_unique<TestMediaController>();
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state =
media_session::mojom::MediaPlaybackState::kPlaying;
session_info->is_controllable = true;
GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
session_info.Clone());
// The item will receive a MediaSessionInfoChanged.
GetItem()->MediaSessionInfoChanged(session_info.Clone());
// The item should still be frozen, and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
// Update the metadata.
media_session::MediaMetadata metadata;
metadata.title = u"title2";
metadata.artist = u"artist2";
GetItem()->MediaSessionMetadataChanged(metadata);
// The item should still be frozen, and waiting for a new image.
EXPECT_TRUE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title", title_label()->GetText());
EXPECT_EQ(u"artist", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
// Once we receive artwork, the item should unfreeze.
EXPECT_CALL(unfrozen_callback, Run);
SkBitmap new_image;
new_image.allocN32Pixels(10, 10);
new_image.eraseColor(SK_ColorYELLOW);
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, new_image);
EXPECT_FALSE(GetItem()->frozen());
testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(u"title2", title_label()->GetText());
EXPECT_EQ(u"artist2", artist_label()->GetText());
EXPECT_FALSE(GetArtworkImage().isNull());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ForcedExpandedState) {
// Make the view expandable.
EnableAllActions();
// Force it to be expanded.
bool expanded_state = true;
view()->SetForcedExpandedState(&expanded_state);
EXPECT_TRUE(GetActuallyExpanded());
// Since it's forced, clicking on the header should not toggle the expanded
// state.
SimulateHeaderClick();
EXPECT_TRUE(GetActuallyExpanded());
// Force it to be not expanded.
expanded_state = false;
view()->SetForcedExpandedState(&expanded_state);
EXPECT_FALSE(GetActuallyExpanded());
// Since it's forced, clicking on the header should not toggle the expanded
// state.
SimulateHeaderClick();
EXPECT_FALSE(GetActuallyExpanded());
// Stop forcing expanded state.
view()->SetForcedExpandedState(nullptr);
EXPECT_FALSE(GetActuallyExpanded());
// Clicking on the header should toggle the expanded state.
SimulateHeaderClick();
EXPECT_TRUE(GetActuallyExpanded());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, AllowsHidingOfAppIcon) {
MediaNotificationViewImpl shows_icon(&container(), nullptr, nullptr,
std::u16string(), kViewWidth,
/*should_show_icon=*/true);
MediaNotificationViewImpl hides_icon(&container(), nullptr, nullptr,
std::u16string(), kViewWidth,
/*should_show_icon=*/false);
EXPECT_TRUE(
GetHeaderRow(&shows_icon)->app_icon_view_for_testing()->GetVisible());
EXPECT_FALSE(
GetHeaderRow(&hides_icon)->app_icon_view_for_testing()->GetVisible());
}
TEST_F(MAYBE_MediaNotificationViewImplTest, ClickHeader_NotifyContainer) {
EXPECT_CALL(container(), OnHeaderClicked());
SimulateHeaderClick();
}
} // namespace media_message_center