1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/media_message_center/media_notification_view_impl.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/containers/flat_set.h"
12 #include "base/macros.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/test/metrics/histogram_tester.h"
15 #include "base/test/mock_callback.h"
16 #include "base/test/task_environment.h"
17 #include "base/unguessable_token.h"
18 #include "build/build_config.h"
19 #include "components/media_message_center/media_notification_background_impl.h"
20 #include "components/media_message_center/media_notification_constants.h"
21 #include "components/media_message_center/media_notification_container.h"
22 #include "components/media_message_center/media_notification_controller.h"
23 #include "components/media_message_center/media_session_notification_item.h"
24 #include "services/media_session/public/cpp/test/test_media_controller.h"
25 #include "services/media_session/public/mojom/audio_focus.mojom.h"
26 #include "services/media_session/public/mojom/media_session.mojom.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "ui/accessibility/ax_enums.mojom.h"
29 #include "ui/accessibility/ax_node_data.h"
30 #include "ui/events/base_event_utils.h"
31 #include "ui/message_center/message_center.h"
32 #include "ui/message_center/public/cpp/message_center_constants.h"
33 #include "ui/message_center/views/message_view_factory.h"
34 #include "ui/message_center/views/notification_control_buttons_view.h"
35 #include "ui/message_center/views/notification_header_view.h"
36 #include "ui/views/controls/image_view.h"
37 #include "ui/views/test/button_test_api.h"
38 #include "ui/views/test/views_test_base.h"
39
40 namespace media_message_center {
41
42 using media_session::mojom::MediaSessionAction;
43 using media_session::test::TestMediaController;
44 using testing::_;
45 using testing::Expectation;
46 using testing::Invoke;
47
48 namespace {
49
50 // The icons size is 24 and INSETS_VECTOR_IMAGE_BUTTON will add padding around
51 // the image.
52 const int kMediaButtonIconSize = 24;
53
54 // The title artist row should always have the same height.
55 const int kMediaTitleArtistRowExpectedHeight = 48;
56
57 const char kTestDefaultAppName[] = "default app name";
58 const char kTestAppName[] = "app name";
59
60 const gfx::Size kWidgetSize(500, 500);
61
62 constexpr int kViewWidth = 400;
63 constexpr int kViewArtworkWidth = kViewWidth * 0.4;
64 const gfx::Size kViewSize(kViewWidth, 400);
65
66 class MockMediaNotificationController : public MediaNotificationController {
67 public:
68 MockMediaNotificationController() = default;
69 ~MockMediaNotificationController() = default;
70
71 // MediaNotificationController implementation.
72 MOCK_METHOD(void, ShowNotification, (const std::string& id));
73 MOCK_METHOD(void, HideNotification, (const std::string& id));
74 MOCK_METHOD(void, RemoveItem, (const std::string& id));
GetTaskRunner() const75 scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override {
76 return nullptr;
77 }
78 MOCK_METHOD(void,
79 LogMediaSessionActionButtonPressed,
80 (const std::string& id,
81 media_session::mojom::MediaSessionAction action));
82
83 private:
84 DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationController);
85 };
86
87 class MockMediaNotificationContainer : public MediaNotificationContainer {
88 public:
89 MockMediaNotificationContainer() = default;
90 ~MockMediaNotificationContainer() = default;
91
92 // MediaNotificationContainer implementation.
93 MOCK_METHOD1(OnExpanded, void(bool expanded));
94 MOCK_METHOD1(
95 OnMediaSessionInfoChanged,
96 void(const media_session::mojom::MediaSessionInfoPtr& session_info));
97 MOCK_METHOD1(OnMediaSessionMetadataChanged,
98 void(const media_session::MediaMetadata& metadata));
99 MOCK_METHOD1(OnVisibleActionsChanged,
100 void(const base::flat_set<MediaSessionAction>& actions));
101 MOCK_METHOD1(OnMediaArtworkChanged, void(const gfx::ImageSkia& image));
102 MOCK_METHOD2(OnColorsChanged, void(SkColor foreground, SkColor background));
103 MOCK_METHOD0(OnHeaderClicked, void());
104
view() const105 MediaNotificationViewImpl* view() const { return view_; }
SetView(MediaNotificationViewImpl * view)106 void SetView(MediaNotificationViewImpl* view) { view_ = view; }
107
108 private:
109 MediaNotificationViewImpl* view_;
110
111 DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationContainer);
112 };
113
114 } // namespace
115
116 class MediaNotificationViewImplTest : public views::ViewsTestBase {
117 public:
MediaNotificationViewImplTest()118 MediaNotificationViewImplTest()
119 : views::ViewsTestBase(
120 base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
121 ~MediaNotificationViewImplTest() override = default;
122
SetUp()123 void SetUp() override {
124 views::ViewsTestBase::SetUp();
125
126 request_id_ = base::UnguessableToken::Create();
127
128 // Create a new MediaNotificationViewImpl whenever the
129 // MediaSessionNotificationItem says to show the notification.
130 EXPECT_CALL(controller_, ShowNotification(request_id_.ToString()))
131 .WillRepeatedly(InvokeWithoutArgs(
132 this, &MediaNotificationViewImplTest::CreateView));
133
134 // Create a widget to show on the screen for testing screen coordinates and
135 // focus.
136 widget_ = std::make_unique<views::Widget>();
137 views::Widget::InitParams params =
138 CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
139 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
140 params.bounds = gfx::Rect(kWidgetSize);
141 widget_->Init(std::move(params));
142 widget_->Show();
143
144 CreateViewFromMediaSessionInfo(
145 media_session::mojom::MediaSessionInfo::New());
146 }
147
CreateViewFromMediaSessionInfo(media_session::mojom::MediaSessionInfoPtr session_info)148 void CreateViewFromMediaSessionInfo(
149 media_session::mojom::MediaSessionInfoPtr session_info) {
150 session_info->is_controllable = true;
151 mojo::Remote<media_session::mojom::MediaController> controller;
152 item_ = std::make_unique<MediaSessionNotificationItem>(
153 &controller_, request_id_.ToString(), std::string(),
154 std::move(controller), std::move(session_info));
155
156 // Update the metadata.
157 media_session::MediaMetadata metadata;
158 metadata.title = base::ASCIIToUTF16("title");
159 metadata.artist = base::ASCIIToUTF16("artist");
160 item_->MediaSessionMetadataChanged(metadata);
161
162 // Inject the test media controller into the item.
163 media_controller_ = std::make_unique<TestMediaController>();
164 item_->SetMediaControllerForTesting(
165 media_controller_->CreateMediaControllerRemote());
166 }
167
TearDown()168 void TearDown() override {
169 container_.SetView(nullptr);
170 widget_.reset();
171
172 actions_.clear();
173
174 views::ViewsTestBase::TearDown();
175 }
176
EnableAllActions()177 void EnableAllActions() {
178 actions_.insert(MediaSessionAction::kPlay);
179 actions_.insert(MediaSessionAction::kPause);
180 actions_.insert(MediaSessionAction::kPreviousTrack);
181 actions_.insert(MediaSessionAction::kNextTrack);
182 actions_.insert(MediaSessionAction::kSeekBackward);
183 actions_.insert(MediaSessionAction::kSeekForward);
184 actions_.insert(MediaSessionAction::kStop);
185 actions_.insert(MediaSessionAction::kEnterPictureInPicture);
186 actions_.insert(MediaSessionAction::kExitPictureInPicture);
187
188 NotifyUpdatedActions();
189 }
190
EnableAction(MediaSessionAction action)191 void EnableAction(MediaSessionAction action) {
192 actions_.insert(action);
193 NotifyUpdatedActions();
194 }
195
DisableAction(MediaSessionAction action)196 void DisableAction(MediaSessionAction action) {
197 actions_.erase(action);
198 NotifyUpdatedActions();
199 }
200
container()201 MockMediaNotificationContainer& container() { return container_; }
202
controller()203 MockMediaNotificationController& controller() { return controller_; }
204
view() const205 MediaNotificationViewImpl* view() const { return container_.view(); }
206
media_controller() const207 TestMediaController* media_controller() const {
208 return media_controller_.get();
209 }
210
GetHeaderRow(MediaNotificationViewImpl * view) const211 message_center::NotificationHeaderView* GetHeaderRow(
212 MediaNotificationViewImpl* view) const {
213 return view->header_row_;
214 }
215
header_row() const216 message_center::NotificationHeaderView* header_row() const {
217 return GetHeaderRow(view());
218 }
219
accessible_name() const220 const base::string16& accessible_name() const {
221 return view()->accessible_name_;
222 }
223
button_row() const224 views::View* button_row() const { return view()->button_row_; }
225
playback_button_container() const226 const views::View* playback_button_container() const {
227 return view()->playback_button_container_;
228 }
229
title_artist_row() const230 views::View* title_artist_row() const { return view()->title_artist_row_; }
231
title_label() const232 views::Label* title_label() const { return view()->title_label_; }
233
artist_label() const234 views::Label* artist_label() const { return view()->artist_label_; }
235
GetButtonForAction(MediaSessionAction action) const236 views::Button* GetButtonForAction(MediaSessionAction action) const {
237 auto buttons = view()->get_buttons_for_testing();
238 const auto i = std::find_if(
239 buttons.begin(), buttons.end(), [action](const views::View* v) {
240 return views::Button::AsButton(v)->tag() == static_cast<int>(action);
241 });
242 return (i == buttons.end()) ? nullptr : views::Button::AsButton(*i);
243 }
244
IsActionButtonVisible(MediaSessionAction action) const245 bool IsActionButtonVisible(MediaSessionAction action) const {
246 return GetButtonForAction(action)->GetVisible();
247 }
248
GetItem() const249 MediaSessionNotificationItem* GetItem() const { return item_.get(); }
250
GetArtworkImage() const251 const gfx::ImageSkia& GetArtworkImage() const {
252 return static_cast<MediaNotificationBackgroundImpl*>(
253 view()->GetMediaNotificationBackground())
254 ->artwork_;
255 }
256
GetAppIcon() const257 const gfx::ImageSkia& GetAppIcon() const {
258 return header_row()->app_icon_for_testing();
259 }
260
expand_button_enabled() const261 bool expand_button_enabled() const {
262 return header_row()->expand_button()->GetVisible();
263 }
264
IsActuallyExpanded() const265 bool IsActuallyExpanded() const { return view()->IsActuallyExpanded(); }
266
SimulateButtonClick(MediaSessionAction action)267 void SimulateButtonClick(MediaSessionAction action) {
268 views::Button* button = GetButtonForAction(action);
269 EXPECT_TRUE(button->GetVisible());
270
271 views::test::ButtonTestApi(button).NotifyClick(
272 ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
273 ui::EventTimeForNow(), 0, 0));
274 }
275
SimulateHeaderClick()276 void SimulateHeaderClick() {
277 views::test::ButtonTestApi(header_row())
278 .NotifyClick(ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(),
279 gfx::Point(), ui::EventTimeForNow(), 0, 0));
280 }
281
SimulateTab()282 void SimulateTab() {
283 ui::KeyEvent pressed_tab(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
284 view()->GetFocusManager()->OnKeyEvent(pressed_tab);
285 }
286
ExpectHistogramActionRecorded(MediaSessionAction action)287 void ExpectHistogramActionRecorded(MediaSessionAction action) {
288 histogram_tester_.ExpectUniqueSample(
289 MediaSessionNotificationItem::kUserActionHistogramName,
290 static_cast<base::HistogramBase::Sample>(action), 1);
291 }
292
ExpectHistogramArtworkRecorded(bool present,int count)293 void ExpectHistogramArtworkRecorded(bool present, int count) {
294 histogram_tester_.ExpectBucketCount(
295 MediaNotificationViewImpl::kArtworkHistogramName,
296 static_cast<base::HistogramBase::Sample>(present), count);
297 }
298
ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata metadata,int count)299 void ExpectHistogramMetadataRecorded(
300 MediaNotificationViewImpl::Metadata metadata,
301 int count) {
302 histogram_tester_.ExpectBucketCount(
303 MediaNotificationViewImpl::kMetadataHistogramName,
304 static_cast<base::HistogramBase::Sample>(metadata), count);
305 }
306
AdvanceClockMilliseconds(int milliseconds)307 void AdvanceClockMilliseconds(int milliseconds) {
308 task_environment()->FastForwardBy(
309 base::TimeDelta::FromMilliseconds(milliseconds));
310 }
311
312 private:
NotifyUpdatedActions()313 void NotifyUpdatedActions() {
314 item_->MediaSessionActionsChanged(
315 std::vector<MediaSessionAction>(actions_.begin(), actions_.end()));
316 }
317
CreateView()318 void CreateView() {
319 // Create a MediaNotificationViewImpl.
320 auto view = std::make_unique<MediaNotificationViewImpl>(
321 &container_, item_->GetWeakPtr(),
322 nullptr /* header_row_controls_view */,
323 base::ASCIIToUTF16(kTestDefaultAppName), kViewWidth,
324 /*should_show_icon=*/true);
325 view->SetSize(kViewSize);
326
327 // Display it in |widget_|. Widget now owns |view|.
328 // And associate it with |container_|.
329 container_.SetView(widget_->SetContentsView(std::move(view)));
330 }
331
332 base::UnguessableToken request_id_;
333
334 base::HistogramTester histogram_tester_;
335
336 base::flat_set<MediaSessionAction> actions_;
337
338 std::unique_ptr<TestMediaController> media_controller_;
339 MockMediaNotificationContainer container_;
340 MockMediaNotificationController controller_;
341 std::unique_ptr<MediaSessionNotificationItem> item_;
342 std::unique_ptr<views::Widget> widget_;
343
344 DISALLOW_COPY_AND_ASSIGN(MediaNotificationViewImplTest);
345 };
346
347 // TODO(crbug.com/1009287): many of these tests are failing on TSan builds.
348 #if defined(THREAD_SANITIZER)
349 #define MAYBE_MediaNotificationViewImplTest \
350 DISABLED_MediaNotificationViewImplTest
351 class DISABLED_MediaNotificationViewImplTest
352 : public MediaNotificationViewImplTest {};
353 #else
354 #define MAYBE_MediaNotificationViewImplTest MediaNotificationViewImplTest
355 #endif
356
TEST_F(MAYBE_MediaNotificationViewImplTest,ButtonsSanityCheck)357 TEST_F(MAYBE_MediaNotificationViewImplTest, ButtonsSanityCheck) {
358 view()->SetExpanded(true);
359
360 EnableAllActions();
361
362 EXPECT_TRUE(button_row()->GetVisible());
363 EXPECT_GT(button_row()->width(), 0);
364 EXPECT_GT(button_row()->height(), 0);
365
366 auto buttons = view()->get_buttons_for_testing();
367 EXPECT_EQ(6u, buttons.size());
368
369 for (auto* button : buttons) {
370 EXPECT_TRUE(button->GetVisible());
371 EXPECT_LT(kMediaButtonIconSize, button->width());
372 EXPECT_LT(kMediaButtonIconSize, button->height());
373 EXPECT_FALSE(views::Button::AsButton(button)->GetAccessibleName().empty());
374 }
375
376 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
377 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
378 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
379 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
380 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
381 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kEnterPictureInPicture));
382
383 // |kPause| cannot be present if |kPlay| is.
384 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
385 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kExitPictureInPicture));
386 }
387
388 #if defined(OS_WIN)
389 #define MAYBE_ButtonsFocusCheck DISABLED_ButtonsFocusCheck
390 #else
391 #define MAYBE_ButtonsFocusCheck ButtonsFocusCheck
392 #endif
TEST_F(MAYBE_MediaNotificationViewImplTest,MAYBE_ButtonsFocusCheck)393 TEST_F(MAYBE_MediaNotificationViewImplTest, MAYBE_ButtonsFocusCheck) {
394 // Expand and enable all actions to show all buttons.
395 view()->SetExpanded(true);
396 EnableAllActions();
397
398 views::FocusManager* focus_manager = view()->GetFocusManager();
399
400 {
401 // Focus the first action button.
402 auto* button = GetButtonForAction(MediaSessionAction::kPreviousTrack);
403 focus_manager->SetFocusedView(button);
404 EXPECT_EQ(button, focus_manager->GetFocusedView());
405 }
406
407 SimulateTab();
408 EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekBackward),
409 focus_manager->GetFocusedView());
410
411 SimulateTab();
412 EXPECT_EQ(GetButtonForAction(MediaSessionAction::kPlay),
413 focus_manager->GetFocusedView());
414
415 SimulateTab();
416 EXPECT_EQ(GetButtonForAction(MediaSessionAction::kSeekForward),
417 focus_manager->GetFocusedView());
418
419 SimulateTab();
420 EXPECT_EQ(GetButtonForAction(MediaSessionAction::kNextTrack),
421 focus_manager->GetFocusedView());
422 }
423
TEST_F(MAYBE_MediaNotificationViewImplTest,PlayPauseButtonTooltipCheck)424 TEST_F(MAYBE_MediaNotificationViewImplTest, PlayPauseButtonTooltipCheck) {
425 EnableAction(MediaSessionAction::kPlay);
426 EnableAction(MediaSessionAction::kPause);
427
428 EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
429
430 auto* button = GetButtonForAction(MediaSessionAction::kPlay);
431 base::string16 tooltip = button->GetTooltipText(gfx::Point());
432 EXPECT_FALSE(tooltip.empty());
433
434 media_session::mojom::MediaSessionInfoPtr session_info(
435 media_session::mojom::MediaSessionInfo::New());
436 session_info->playback_state =
437 media_session::mojom::MediaPlaybackState::kPlaying;
438 session_info->is_controllable = true;
439 GetItem()->MediaSessionInfoChanged(session_info.Clone());
440
441 base::string16 new_tooltip = button->GetTooltipText(gfx::Point());
442 EXPECT_FALSE(new_tooltip.empty());
443 EXPECT_NE(tooltip, new_tooltip);
444 }
445
TEST_F(MAYBE_MediaNotificationViewImplTest,NextTrackButtonClick)446 TEST_F(MAYBE_MediaNotificationViewImplTest, NextTrackButtonClick) {
447 EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
448 _, MediaSessionAction::kNextTrack));
449 EnableAction(MediaSessionAction::kNextTrack);
450
451 EXPECT_EQ(0, media_controller()->next_track_count());
452
453 SimulateButtonClick(MediaSessionAction::kNextTrack);
454 GetItem()->FlushForTesting();
455
456 EXPECT_EQ(1, media_controller()->next_track_count());
457 ExpectHistogramActionRecorded(MediaSessionAction::kNextTrack);
458 }
459
TEST_F(MAYBE_MediaNotificationViewImplTest,PlayButtonClick)460 TEST_F(MAYBE_MediaNotificationViewImplTest, PlayButtonClick) {
461 EXPECT_CALL(controller(),
462 LogMediaSessionActionButtonPressed(_, MediaSessionAction::kPlay));
463 EnableAction(MediaSessionAction::kPlay);
464
465 EXPECT_EQ(0, media_controller()->resume_count());
466
467 SimulateButtonClick(MediaSessionAction::kPlay);
468 GetItem()->FlushForTesting();
469
470 EXPECT_EQ(1, media_controller()->resume_count());
471 ExpectHistogramActionRecorded(MediaSessionAction::kPlay);
472 }
473
TEST_F(MAYBE_MediaNotificationViewImplTest,PauseButtonClick)474 TEST_F(MAYBE_MediaNotificationViewImplTest, PauseButtonClick) {
475 EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
476 _, MediaSessionAction::kPause));
477 EnableAction(MediaSessionAction::kPause);
478 EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
479
480 EXPECT_EQ(0, media_controller()->suspend_count());
481
482 media_session::mojom::MediaSessionInfoPtr session_info(
483 media_session::mojom::MediaSessionInfo::New());
484 session_info->playback_state =
485 media_session::mojom::MediaPlaybackState::kPlaying;
486 session_info->is_controllable = true;
487 GetItem()->MediaSessionInfoChanged(session_info.Clone());
488
489 SimulateButtonClick(MediaSessionAction::kPause);
490 GetItem()->FlushForTesting();
491
492 EXPECT_EQ(1, media_controller()->suspend_count());
493 ExpectHistogramActionRecorded(MediaSessionAction::kPause);
494 }
495
TEST_F(MAYBE_MediaNotificationViewImplTest,PreviousTrackButtonClick)496 TEST_F(MAYBE_MediaNotificationViewImplTest, PreviousTrackButtonClick) {
497 EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
498 _, MediaSessionAction::kPreviousTrack));
499 EnableAction(MediaSessionAction::kPreviousTrack);
500
501 EXPECT_EQ(0, media_controller()->previous_track_count());
502
503 SimulateButtonClick(MediaSessionAction::kPreviousTrack);
504 GetItem()->FlushForTesting();
505
506 EXPECT_EQ(1, media_controller()->previous_track_count());
507 ExpectHistogramActionRecorded(MediaSessionAction::kPreviousTrack);
508 }
509
TEST_F(MAYBE_MediaNotificationViewImplTest,SeekBackwardButtonClick)510 TEST_F(MAYBE_MediaNotificationViewImplTest, SeekBackwardButtonClick) {
511 EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
512 _, MediaSessionAction::kSeekBackward));
513 EnableAction(MediaSessionAction::kSeekBackward);
514
515 EXPECT_EQ(0, media_controller()->seek_backward_count());
516
517 SimulateButtonClick(MediaSessionAction::kSeekBackward);
518 GetItem()->FlushForTesting();
519
520 EXPECT_EQ(1, media_controller()->seek_backward_count());
521 ExpectHistogramActionRecorded(MediaSessionAction::kSeekBackward);
522 }
523
TEST_F(MAYBE_MediaNotificationViewImplTest,SeekForwardButtonClick)524 TEST_F(MAYBE_MediaNotificationViewImplTest, SeekForwardButtonClick) {
525 EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
526 _, MediaSessionAction::kSeekForward));
527 EnableAction(MediaSessionAction::kSeekForward);
528
529 EXPECT_EQ(0, media_controller()->seek_forward_count());
530
531 SimulateButtonClick(MediaSessionAction::kSeekForward);
532 GetItem()->FlushForTesting();
533
534 EXPECT_EQ(1, media_controller()->seek_forward_count());
535 ExpectHistogramActionRecorded(MediaSessionAction::kSeekForward);
536 }
537
TEST_F(MAYBE_MediaNotificationViewImplTest,PlayToggle_FromObserver_Empty)538 TEST_F(MAYBE_MediaNotificationViewImplTest, PlayToggle_FromObserver_Empty) {
539 EnableAction(MediaSessionAction::kPlay);
540
541 {
542 views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
543 GetButtonForAction(MediaSessionAction::kPlay));
544 ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
545 EXPECT_FALSE(button->GetToggled());
546 }
547
548 view()->UpdateWithMediaSessionInfo(
549 media_session::mojom::MediaSessionInfo::New());
550
551 {
552 views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
553 GetButtonForAction(MediaSessionAction::kPlay));
554 ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
555 EXPECT_FALSE(button->GetToggled());
556 }
557 }
558
TEST_F(MAYBE_MediaNotificationViewImplTest,PlayToggle_FromObserver_PlaybackState)559 TEST_F(MAYBE_MediaNotificationViewImplTest,
560 PlayToggle_FromObserver_PlaybackState) {
561 EnableAction(MediaSessionAction::kPlay);
562 EnableAction(MediaSessionAction::kPause);
563
564 {
565 views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
566 GetButtonForAction(MediaSessionAction::kPlay));
567 ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
568 EXPECT_FALSE(button->GetToggled());
569 }
570
571 media_session::mojom::MediaSessionInfoPtr session_info(
572 media_session::mojom::MediaSessionInfo::New());
573
574 session_info->playback_state =
575 media_session::mojom::MediaPlaybackState::kPlaying;
576 view()->UpdateWithMediaSessionInfo(session_info.Clone());
577
578 {
579 views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
580 GetButtonForAction(MediaSessionAction::kPause));
581 ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
582 EXPECT_TRUE(button->GetToggled());
583 }
584
585 session_info->playback_state =
586 media_session::mojom::MediaPlaybackState::kPaused;
587 view()->UpdateWithMediaSessionInfo(session_info.Clone());
588
589 {
590 views::ToggleImageButton* button = static_cast<views::ToggleImageButton*>(
591 GetButtonForAction(MediaSessionAction::kPlay));
592 ASSERT_EQ(views::ToggleImageButton::kViewClassName, button->GetClassName());
593 EXPECT_FALSE(button->GetToggled());
594 }
595 }
596
TEST_F(MAYBE_MediaNotificationViewImplTest,MetadataIsDisplayed)597 TEST_F(MAYBE_MediaNotificationViewImplTest, MetadataIsDisplayed) {
598 view()->SetExpanded(true);
599
600 EnableAllActions();
601
602 EXPECT_TRUE(title_artist_row()->GetVisible());
603 EXPECT_TRUE(title_label()->GetVisible());
604 EXPECT_TRUE(artist_label()->GetVisible());
605
606 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
607 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
608
609 EXPECT_EQ(kMediaTitleArtistRowExpectedHeight, title_artist_row()->height());
610 }
611
TEST_F(MAYBE_MediaNotificationViewImplTest,UpdateMetadata_FromObserver)612 TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateMetadata_FromObserver) {
613 EnableAllActions();
614
615 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kTitle,
616 1);
617 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kArtist,
618 1);
619 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kAlbum,
620 0);
621 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kCount,
622 1);
623
624 EXPECT_FALSE(header_row()->summary_text_for_testing()->GetVisible());
625
626 media_session::MediaMetadata metadata;
627 metadata.title = base::ASCIIToUTF16("title2");
628 metadata.artist = base::ASCIIToUTF16("artist2");
629 metadata.album = base::ASCIIToUTF16("album");
630
631 EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_));
632 GetItem()->MediaSessionMetadataChanged(metadata);
633 view()->SetExpanded(true);
634
635 EXPECT_TRUE(title_artist_row()->GetVisible());
636 EXPECT_TRUE(title_label()->GetVisible());
637 EXPECT_TRUE(artist_label()->GetVisible());
638 EXPECT_TRUE(header_row()->summary_text_for_testing()->GetVisible());
639
640 EXPECT_EQ(metadata.title, title_label()->GetText());
641 EXPECT_EQ(metadata.artist, artist_label()->GetText());
642 EXPECT_EQ(metadata.album,
643 header_row()->summary_text_for_testing()->GetText());
644
645 EXPECT_EQ(kMediaTitleArtistRowExpectedHeight, title_artist_row()->height());
646
647 EXPECT_EQ(base::ASCIIToUTF16("title2 - artist2 - album"), accessible_name());
648
649 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kTitle,
650 2);
651 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kArtist,
652 2);
653 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kAlbum,
654 1);
655 ExpectHistogramMetadataRecorded(MediaNotificationViewImpl::Metadata::kCount,
656 2);
657 }
658
TEST_F(MAYBE_MediaNotificationViewImplTest,UpdateMetadata_AppName)659 TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateMetadata_AppName) {
660 EXPECT_EQ(base::ASCIIToUTF16(kTestDefaultAppName),
661 header_row()->app_name_for_testing());
662
663 {
664 media_session::MediaMetadata metadata;
665 metadata.title = base::ASCIIToUTF16("title");
666 metadata.artist = base::ASCIIToUTF16("artist");
667 metadata.source_title = base::ASCIIToUTF16(kTestAppName);
668 GetItem()->MediaSessionMetadataChanged(metadata);
669 }
670
671 EXPECT_EQ(base::ASCIIToUTF16(kTestAppName),
672 header_row()->app_name_for_testing());
673
674 {
675 media_session::MediaMetadata metadata;
676 metadata.title = base::ASCIIToUTF16("title");
677 metadata.artist = base::ASCIIToUTF16("artist");
678 GetItem()->MediaSessionMetadataChanged(metadata);
679 }
680
681 EXPECT_EQ(base::ASCIIToUTF16(kTestDefaultAppName),
682 header_row()->app_name_for_testing());
683 }
684
TEST_F(MAYBE_MediaNotificationViewImplTest,Buttons_WhenCollapsed)685 TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
686 EXPECT_CALL(
687 container(),
688 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
689 {MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
690 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
691 MediaSessionAction::kSeekForward,
692 MediaSessionAction::kEnterPictureInPicture})));
693 EnableAllActions();
694 view()->SetExpanded(false);
695 testing::Mock::VerifyAndClearExpectations(&container());
696
697 EXPECT_FALSE(IsActuallyExpanded());
698
699 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPlay));
700 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
701 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
702 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
703 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
704
705 EXPECT_CALL(
706 container(),
707 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
708 {MediaSessionAction::kPlay, MediaSessionAction::kSeekBackward,
709 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekForward,
710 MediaSessionAction::kEnterPictureInPicture})));
711 DisableAction(MediaSessionAction::kPreviousTrack);
712 testing::Mock::VerifyAndClearExpectations(&container());
713 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
714
715 EXPECT_CALL(
716 container(),
717 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
718 {MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
719 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
720 MediaSessionAction::kSeekForward,
721 MediaSessionAction::kEnterPictureInPicture})));
722 EnableAction(MediaSessionAction::kPreviousTrack);
723 testing::Mock::VerifyAndClearExpectations(&container());
724 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
725
726 EXPECT_CALL(
727 container(),
728 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
729 {MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
730 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
731 MediaSessionAction::kEnterPictureInPicture})));
732 DisableAction(MediaSessionAction::kSeekForward);
733 testing::Mock::VerifyAndClearExpectations(&container());
734 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
735
736 EXPECT_CALL(
737 container(),
738 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
739 {MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
740 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
741 MediaSessionAction::kSeekForward,
742 MediaSessionAction::kEnterPictureInPicture})));
743 EnableAction(MediaSessionAction::kSeekForward);
744 testing::Mock::VerifyAndClearExpectations(&container());
745 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
746 }
747
TEST_F(MAYBE_MediaNotificationViewImplTest,Buttons_WhenExpanded)748 TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenExpanded) {
749 EXPECT_CALL(
750 container(),
751 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
752 {MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
753 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
754 MediaSessionAction::kSeekForward,
755 MediaSessionAction::kEnterPictureInPicture})));
756 EnableAllActions();
757 testing::Mock::VerifyAndClearExpectations(&container());
758
759 EXPECT_CALL(
760 container(),
761 OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
762 {MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
763 MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
764 MediaSessionAction::kSeekForward,
765 MediaSessionAction::kEnterPictureInPicture})));
766 view()->SetExpanded(true);
767 testing::Mock::VerifyAndClearExpectations(&container());
768
769 EXPECT_TRUE(IsActuallyExpanded());
770
771 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPlay));
772 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
773 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
774 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
775 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
776 }
777
TEST_F(MAYBE_MediaNotificationViewImplTest,ClickHeader_ToggleExpand)778 TEST_F(MAYBE_MediaNotificationViewImplTest, ClickHeader_ToggleExpand) {
779 view()->SetExpanded(true);
780 EnableAllActions();
781
782 EXPECT_TRUE(IsActuallyExpanded());
783
784 SimulateHeaderClick();
785
786 EXPECT_FALSE(IsActuallyExpanded());
787
788 SimulateHeaderClick();
789
790 EXPECT_TRUE(IsActuallyExpanded());
791 }
792
TEST_F(MAYBE_MediaNotificationViewImplTest,ActionButtonsHiddenByDefault)793 TEST_F(MAYBE_MediaNotificationViewImplTest, ActionButtonsHiddenByDefault) {
794 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPlay));
795 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
796 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
797 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
798 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekBackward));
799 }
800
TEST_F(MAYBE_MediaNotificationViewImplTest,ActionButtonsToggleVisibility)801 TEST_F(MAYBE_MediaNotificationViewImplTest, ActionButtonsToggleVisibility) {
802 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
803
804 EnableAction(MediaSessionAction::kNextTrack);
805
806 EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
807
808 DisableAction(MediaSessionAction::kNextTrack);
809
810 EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kNextTrack));
811 }
812
TEST_F(MAYBE_MediaNotificationViewImplTest,UpdateArtworkFromItem)813 TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateArtworkFromItem) {
814 int title_artist_width = title_artist_row()->width();
815 const SkColor accent = header_row()->accent_color_for_testing().value();
816 gfx::Size size = view()->size();
817 EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(2);
818 EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(2);
819
820 SkBitmap image;
821 image.allocN32Pixels(10, 10);
822 image.eraseColor(SK_ColorGREEN);
823
824 EXPECT_TRUE(GetArtworkImage().isNull());
825
826 GetItem()->MediaControllerImageChanged(
827 media_session::mojom::MediaSessionImageType::kArtwork, image);
828
829 ExpectHistogramArtworkRecorded(true, 1);
830
831 // Ensure the title artist row has a small width than before now that we
832 // have artwork.
833 EXPECT_GT(title_artist_width, title_artist_row()->width());
834
835 // Ensure that the title artist row does not extend into the artwork bounds.
836 EXPECT_LE(kViewWidth - kViewArtworkWidth, title_artist_row()->width());
837
838 // Ensure that the image is displayed in the background artwork and that the
839 // size of the notification was not affected.
840 EXPECT_FALSE(GetArtworkImage().isNull());
841 EXPECT_EQ(gfx::Size(10, 10), GetArtworkImage().size());
842 EXPECT_EQ(size, view()->size());
843 auto accent_color = header_row()->accent_color_for_testing();
844 ASSERT_TRUE(accent_color.has_value());
845 EXPECT_NE(accent, accent_color.value());
846
847 GetItem()->MediaControllerImageChanged(
848 media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
849
850 ExpectHistogramArtworkRecorded(false, 1);
851
852 // Ensure the title artist row goes back to the original width now that we
853 // do not have any artwork.
854 EXPECT_EQ(title_artist_width, title_artist_row()->width());
855
856 // Ensure that the background artwork was reset and the size was still not
857 // affected.
858 EXPECT_TRUE(GetArtworkImage().isNull());
859 EXPECT_EQ(size, view()->size());
860 accent_color = header_row()->accent_color_for_testing();
861 ASSERT_TRUE(accent_color.has_value());
862 EXPECT_EQ(accent, accent_color.value());
863 }
864
TEST_F(MAYBE_MediaNotificationViewImplTest,ExpandableDefaultState)865 TEST_F(MAYBE_MediaNotificationViewImplTest, ExpandableDefaultState) {
866 EXPECT_FALSE(IsActuallyExpanded());
867 EXPECT_FALSE(expand_button_enabled());
868 }
869
TEST_F(MAYBE_MediaNotificationViewImplTest,ExpandablePlayPauseActionCountsOnce)870 TEST_F(MAYBE_MediaNotificationViewImplTest,
871 ExpandablePlayPauseActionCountsOnce) {
872 view()->SetExpanded(true);
873
874 EXPECT_FALSE(IsActuallyExpanded());
875 EXPECT_FALSE(expand_button_enabled());
876
877 EnableAction(MediaSessionAction::kPreviousTrack);
878 EnableAction(MediaSessionAction::kNextTrack);
879 EnableAction(MediaSessionAction::kPlay);
880 EnableAction(MediaSessionAction::kPause);
881
882 EXPECT_FALSE(IsActuallyExpanded());
883 EXPECT_FALSE(expand_button_enabled());
884
885 media_session::mojom::MediaSessionInfoPtr session_info(
886 media_session::mojom::MediaSessionInfo::New());
887 session_info->playback_state =
888 media_session::mojom::MediaPlaybackState::kPlaying;
889 view()->UpdateWithMediaSessionInfo(session_info);
890
891 EXPECT_FALSE(IsActuallyExpanded());
892 EXPECT_FALSE(expand_button_enabled());
893
894 EnableAction(MediaSessionAction::kSeekForward);
895
896 EXPECT_TRUE(IsActuallyExpanded());
897 EXPECT_TRUE(expand_button_enabled());
898 }
899
TEST_F(MAYBE_MediaNotificationViewImplTest,BecomeExpandableAndWasNotExpandable)900 TEST_F(MAYBE_MediaNotificationViewImplTest,
901 BecomeExpandableAndWasNotExpandable) {
902 view()->SetExpanded(true);
903
904 EXPECT_FALSE(IsActuallyExpanded());
905 EXPECT_FALSE(expand_button_enabled());
906
907 EnableAllActions();
908
909 EXPECT_TRUE(IsActuallyExpanded());
910 EXPECT_TRUE(expand_button_enabled());
911 }
912
TEST_F(MAYBE_MediaNotificationViewImplTest,BecomeExpandableButWasAlreadyExpandable)913 TEST_F(MAYBE_MediaNotificationViewImplTest,
914 BecomeExpandableButWasAlreadyExpandable) {
915 view()->SetExpanded(true);
916
917 EXPECT_FALSE(IsActuallyExpanded());
918 EXPECT_FALSE(expand_button_enabled());
919
920 EnableAllActions();
921
922 EXPECT_TRUE(IsActuallyExpanded());
923 EXPECT_TRUE(expand_button_enabled());
924
925 DisableAction(MediaSessionAction::kSeekForward);
926
927 EXPECT_TRUE(IsActuallyExpanded());
928 EXPECT_TRUE(expand_button_enabled());
929 }
930
TEST_F(MAYBE_MediaNotificationViewImplTest,BecomeNotExpandableAndWasExpandable)931 TEST_F(MAYBE_MediaNotificationViewImplTest,
932 BecomeNotExpandableAndWasExpandable) {
933 view()->SetExpanded(true);
934
935 EXPECT_FALSE(IsActuallyExpanded());
936 EXPECT_FALSE(expand_button_enabled());
937
938 EnableAllActions();
939
940 EXPECT_TRUE(IsActuallyExpanded());
941 EXPECT_TRUE(expand_button_enabled());
942
943 DisableAction(MediaSessionAction::kPreviousTrack);
944 DisableAction(MediaSessionAction::kNextTrack);
945 DisableAction(MediaSessionAction::kSeekBackward);
946 DisableAction(MediaSessionAction::kSeekForward);
947
948 EXPECT_FALSE(IsActuallyExpanded());
949 EXPECT_FALSE(expand_button_enabled());
950 }
951
TEST_F(MAYBE_MediaNotificationViewImplTest,BecomeNotExpandableButWasAlreadyNotExpandable)952 TEST_F(MAYBE_MediaNotificationViewImplTest,
953 BecomeNotExpandableButWasAlreadyNotExpandable) {
954 view()->SetExpanded(true);
955
956 EXPECT_FALSE(IsActuallyExpanded());
957 EXPECT_FALSE(expand_button_enabled());
958
959 EnableAction(MediaSessionAction::kSeekForward);
960
961 EXPECT_FALSE(IsActuallyExpanded());
962 EXPECT_FALSE(expand_button_enabled());
963 }
964
TEST_F(MAYBE_MediaNotificationViewImplTest,ActionButtonRowSizeAndAlignment)965 TEST_F(MAYBE_MediaNotificationViewImplTest, ActionButtonRowSizeAndAlignment) {
966 EnableAction(MediaSessionAction::kPlay);
967
968 views::Button* button = GetButtonForAction(MediaSessionAction::kPlay);
969 int button_x = button->GetBoundsInScreen().x();
970
971 // When collapsed the button row should be a fixed width.
972 EXPECT_FALSE(IsActuallyExpanded());
973 EXPECT_EQ(124, button_row()->width());
974
975 EnableAllActions();
976 view()->SetExpanded(true);
977
978 // When expanded the button row should be wider and the play button should
979 // have shifted to the left.
980 EXPECT_TRUE(IsActuallyExpanded());
981 EXPECT_LT(124, button_row()->width());
982 EXPECT_GT(button_x, button->GetBoundsInScreen().x());
983 }
984
TEST_F(MAYBE_MediaNotificationViewImplTest,NotifysContainerOfExpandedState)985 TEST_F(MAYBE_MediaNotificationViewImplTest, NotifysContainerOfExpandedState) {
986 // Track the expanded state given to |container_|.
987 bool expanded = false;
988 EXPECT_CALL(container(), OnExpanded(_))
989 .WillRepeatedly(Invoke([&expanded](bool exp) { expanded = exp; }));
990
991 // Expand the view implicitly via |EnableAllActions()|.
992 view()->SetExpanded(true);
993 EnableAllActions();
994 EXPECT_TRUE(expanded);
995
996 // Explicitly contract the view.
997 view()->SetExpanded(false);
998 EXPECT_FALSE(expanded);
999
1000 // Explicitly expand the view.
1001 view()->SetExpanded(true);
1002 EXPECT_TRUE(expanded);
1003
1004 // Implicitly contract the view by removing available actions.
1005 DisableAction(MediaSessionAction::kPreviousTrack);
1006 DisableAction(MediaSessionAction::kNextTrack);
1007 DisableAction(MediaSessionAction::kSeekBackward);
1008 DisableAction(MediaSessionAction::kSeekForward);
1009 EXPECT_FALSE(expanded);
1010 }
1011
TEST_F(MAYBE_MediaNotificationViewImplTest,AccessibleNodeData)1012 TEST_F(MAYBE_MediaNotificationViewImplTest, AccessibleNodeData) {
1013 ui::AXNodeData data;
1014 view()->GetAccessibleNodeData(&data);
1015
1016 EXPECT_TRUE(
1017 data.HasStringAttribute(ax::mojom::StringAttribute::kRoleDescription));
1018 EXPECT_EQ(base::ASCIIToUTF16("title - artist"), accessible_name());
1019 }
1020
TEST_F(MAYBE_MediaNotificationViewImplTest,Freezing_DoNotUpdateMetadata)1021 TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateMetadata) {
1022 media_session::MediaMetadata metadata;
1023 metadata.title = base::ASCIIToUTF16("title2");
1024 metadata.artist = base::ASCIIToUTF16("artist2");
1025 metadata.album = base::ASCIIToUTF16("album");
1026
1027 EXPECT_CALL(container(), OnMediaSessionMetadataChanged(_)).Times(0);
1028 GetItem()->Freeze(base::DoNothing());
1029 GetItem()->MediaSessionMetadataChanged(metadata);
1030
1031 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1032 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1033 }
1034
TEST_F(MAYBE_MediaNotificationViewImplTest,Freezing_DoNotUpdateImage)1035 TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateImage) {
1036 SkBitmap image;
1037 image.allocN32Pixels(10, 10);
1038 image.eraseColor(SK_ColorMAGENTA);
1039 EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(0);
1040 EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(0);
1041
1042 GetItem()->Freeze(base::DoNothing());
1043 GetItem()->MediaControllerImageChanged(
1044 media_session::mojom::MediaSessionImageType::kArtwork, image);
1045
1046 EXPECT_TRUE(GetArtworkImage().isNull());
1047 }
1048
TEST_F(MAYBE_MediaNotificationViewImplTest,Freezing_DoNotUpdatePlaybackState)1049 TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdatePlaybackState) {
1050 EnableAction(MediaSessionAction::kPlay);
1051 EnableAction(MediaSessionAction::kPause);
1052
1053 EXPECT_CALL(container(), OnMediaSessionInfoChanged(_)).Times(0);
1054
1055 GetItem()->Freeze(base::DoNothing());
1056
1057 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1058 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1059
1060 media_session::mojom::MediaSessionInfoPtr session_info(
1061 media_session::mojom::MediaSessionInfo::New());
1062 session_info->playback_state =
1063 media_session::mojom::MediaPlaybackState::kPlaying;
1064 GetItem()->MediaSessionInfoChanged(session_info.Clone());
1065
1066 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1067 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1068 }
1069
TEST_F(MAYBE_MediaNotificationViewImplTest,Freezing_DoNotUpdateActions)1070 TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateActions) {
1071 EXPECT_FALSE(
1072 GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
1073
1074 GetItem()->Freeze(base::DoNothing());
1075 EnableAction(MediaSessionAction::kSeekForward);
1076
1077 EXPECT_FALSE(
1078 GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
1079 }
1080
TEST_F(MAYBE_MediaNotificationViewImplTest,Freezing_DisableInteraction)1081 TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DisableInteraction) {
1082 EnableAllActions();
1083
1084 EXPECT_EQ(0, media_controller()->next_track_count());
1085
1086 GetItem()->Freeze(base::DoNothing());
1087
1088 SimulateButtonClick(MediaSessionAction::kNextTrack);
1089 GetItem()->FlushForTesting();
1090
1091 EXPECT_EQ(0, media_controller()->next_track_count());
1092 }
1093
TEST_F(MAYBE_MediaNotificationViewImplTest,UnfreezingDoesntMissUpdates)1094 TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingDoesntMissUpdates) {
1095 EnableAction(MediaSessionAction::kPlay);
1096 EnableAction(MediaSessionAction::kPause);
1097
1098 // Freeze the item and clear the metadata.
1099 base::MockOnceClosure unfrozen_callback;
1100 EXPECT_CALL(unfrozen_callback, Run).Times(0);
1101 GetItem()->Freeze(unfrozen_callback.Get());
1102 GetItem()->MediaSessionInfoChanged(nullptr);
1103 GetItem()->MediaSessionMetadataChanged(base::nullopt);
1104
1105 // The item should be frozen and the view should contain the old data.
1106 EXPECT_TRUE(GetItem()->frozen());
1107 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1108 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1109 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1110 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1111
1112 // Bind the item to a new controller that's playing instead of paused.
1113 auto new_media_controller = std::make_unique<TestMediaController>();
1114 media_session::mojom::MediaSessionInfoPtr session_info(
1115 media_session::mojom::MediaSessionInfo::New());
1116 session_info->playback_state =
1117 media_session::mojom::MediaPlaybackState::kPlaying;
1118 session_info->is_controllable = true;
1119 GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
1120 session_info.Clone());
1121
1122 // The item will receive a MediaSessionInfoChanged.
1123 GetItem()->MediaSessionInfoChanged(session_info.Clone());
1124
1125 // The item should still be frozen, and the view should contain the old data.
1126 EXPECT_TRUE(GetItem()->frozen());
1127 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1128 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1129 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1130 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1131 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1132
1133 // Update the metadata.
1134 EXPECT_CALL(unfrozen_callback, Run);
1135 media_session::MediaMetadata metadata;
1136 metadata.title = base::ASCIIToUTF16("title2");
1137 metadata.artist = base::ASCIIToUTF16("artist2");
1138 GetItem()->MediaSessionMetadataChanged(metadata);
1139
1140 // The item should no longer be frozen, and we should see the updated data.
1141 EXPECT_FALSE(GetItem()->frozen());
1142 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1143 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
1144 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
1145 EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
1146 EXPECT_EQ(base::ASCIIToUTF16("artist2"), artist_label()->GetText());
1147 }
1148
TEST_F(MAYBE_MediaNotificationViewImplTest,UnfreezingWaitsForArtwork_Timeout)1149 TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForArtwork_Timeout) {
1150 EnableAction(MediaSessionAction::kPlay);
1151 EnableAction(MediaSessionAction::kPause);
1152
1153 // Set an image before freezing.
1154 SkBitmap initial_image;
1155 initial_image.allocN32Pixels(10, 10);
1156 initial_image.eraseColor(SK_ColorMAGENTA);
1157 GetItem()->MediaControllerImageChanged(
1158 media_session::mojom::MediaSessionImageType::kArtwork, initial_image);
1159 EXPECT_FALSE(GetArtworkImage().isNull());
1160
1161 // Freeze the item and clear the metadata.
1162 base::MockOnceClosure unfrozen_callback;
1163 EXPECT_CALL(unfrozen_callback, Run).Times(0);
1164 GetItem()->Freeze(unfrozen_callback.Get());
1165 GetItem()->MediaSessionInfoChanged(nullptr);
1166 GetItem()->MediaSessionMetadataChanged(base::nullopt);
1167 GetItem()->MediaControllerImageChanged(
1168 media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
1169
1170 // The item should be frozen and the view should contain the old data.
1171 EXPECT_TRUE(GetItem()->frozen());
1172 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1173 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1174 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1175 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1176 EXPECT_FALSE(GetArtworkImage().isNull());
1177
1178 // Bind the item to a new controller that's playing instead of paused.
1179 auto new_media_controller = std::make_unique<TestMediaController>();
1180 media_session::mojom::MediaSessionInfoPtr session_info(
1181 media_session::mojom::MediaSessionInfo::New());
1182 session_info->playback_state =
1183 media_session::mojom::MediaPlaybackState::kPlaying;
1184 session_info->is_controllable = true;
1185 GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
1186 session_info.Clone());
1187
1188 // The item will receive a MediaSessionInfoChanged.
1189 GetItem()->MediaSessionInfoChanged(session_info.Clone());
1190
1191 // The item should still be frozen, and the view should contain the old data.
1192 EXPECT_TRUE(GetItem()->frozen());
1193 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1194 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1195 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1196 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1197 EXPECT_FALSE(GetArtworkImage().isNull());
1198
1199 // Update the metadata.
1200 media_session::MediaMetadata metadata;
1201 metadata.title = base::ASCIIToUTF16("title2");
1202 metadata.artist = base::ASCIIToUTF16("artist2");
1203 GetItem()->MediaSessionMetadataChanged(metadata);
1204
1205 // The item should still be frozen, and waiting for a new image.
1206 EXPECT_TRUE(GetItem()->frozen());
1207 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1208 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1209 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1210 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1211 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1212 EXPECT_FALSE(GetArtworkImage().isNull());
1213
1214 // Once the freeze timer fires, the item should unfreeze even if there's no
1215 // artwork.
1216 EXPECT_CALL(unfrozen_callback, Run);
1217 AdvanceClockMilliseconds(2600);
1218
1219 EXPECT_FALSE(GetItem()->frozen());
1220 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1221 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
1222 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
1223 EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
1224 EXPECT_EQ(base::ASCIIToUTF16("artist2"), artist_label()->GetText());
1225 EXPECT_TRUE(GetArtworkImage().isNull());
1226 }
1227
TEST_F(MAYBE_MediaNotificationViewImplTest,UnfreezingWaitsForActions)1228 TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForActions) {
1229 EnableAction(MediaSessionAction::kPlay);
1230 EnableAction(MediaSessionAction::kPause);
1231 EnableAction(MediaSessionAction::kNextTrack);
1232 EnableAction(MediaSessionAction::kPreviousTrack);
1233
1234 // Freeze the item and clear the metadata and actions.
1235 base::MockOnceClosure unfrozen_callback;
1236 EXPECT_CALL(unfrozen_callback, Run).Times(0);
1237 GetItem()->Freeze(unfrozen_callback.Get());
1238 GetItem()->MediaSessionInfoChanged(nullptr);
1239 GetItem()->MediaSessionMetadataChanged(base::nullopt);
1240 DisableAction(MediaSessionAction::kPlay);
1241 DisableAction(MediaSessionAction::kPause);
1242 DisableAction(MediaSessionAction::kNextTrack);
1243 DisableAction(MediaSessionAction::kPreviousTrack);
1244
1245 // The item should be frozen and the view should contain the old data.
1246 EXPECT_TRUE(GetItem()->frozen());
1247 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1248 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1249 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
1250 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
1251 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1252 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1253
1254 // Bind the item to a new controller that's playing instead of paused.
1255 auto new_media_controller = std::make_unique<TestMediaController>();
1256 media_session::mojom::MediaSessionInfoPtr session_info(
1257 media_session::mojom::MediaSessionInfo::New());
1258 session_info->playback_state =
1259 media_session::mojom::MediaPlaybackState::kPlaying;
1260 session_info->is_controllable = true;
1261 GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
1262 session_info.Clone());
1263
1264 // The item will receive a MediaSessionInfoChanged.
1265 GetItem()->MediaSessionInfoChanged(session_info.Clone());
1266
1267 // The item should still be frozen, and the view should contain the old data.
1268 EXPECT_TRUE(GetItem()->frozen());
1269 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1270 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1271 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
1272 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
1273 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1274 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1275
1276 // Update the metadata.
1277 media_session::MediaMetadata metadata;
1278 metadata.title = base::ASCIIToUTF16("title2");
1279 metadata.artist = base::ASCIIToUTF16("artist2");
1280 GetItem()->MediaSessionMetadataChanged(metadata);
1281
1282 // The item should still be frozen, and waiting for new actions.
1283 EXPECT_TRUE(GetItem()->frozen());
1284 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1285 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1286 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1287 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
1288 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
1289 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1290 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1291
1292 // Once we receive actions, the item should unfreeze.
1293 EXPECT_CALL(unfrozen_callback, Run);
1294 EnableAction(MediaSessionAction::kPlay);
1295 EnableAction(MediaSessionAction::kPause);
1296 EnableAction(MediaSessionAction::kSeekForward);
1297 EnableAction(MediaSessionAction::kSeekBackward);
1298
1299 EXPECT_FALSE(GetItem()->frozen());
1300 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1301 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
1302 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
1303 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
1304 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
1305 EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
1306 EXPECT_EQ(base::ASCIIToUTF16("artist2"), artist_label()->GetText());
1307 }
1308
TEST_F(MAYBE_MediaNotificationViewImplTest,UnfreezingWaitsForArtwork_ReceiveArtwork)1309 TEST_F(MAYBE_MediaNotificationViewImplTest,
1310 UnfreezingWaitsForArtwork_ReceiveArtwork) {
1311 EnableAction(MediaSessionAction::kPlay);
1312 EnableAction(MediaSessionAction::kPause);
1313
1314 // Set an image before freezing.
1315 SkBitmap initial_image;
1316 initial_image.allocN32Pixels(10, 10);
1317 initial_image.eraseColor(SK_ColorMAGENTA);
1318 GetItem()->MediaControllerImageChanged(
1319 media_session::mojom::MediaSessionImageType::kArtwork, initial_image);
1320 EXPECT_FALSE(GetArtworkImage().isNull());
1321
1322 // Freeze the item and clear the metadata.
1323 base::MockOnceClosure unfrozen_callback;
1324 EXPECT_CALL(unfrozen_callback, Run).Times(0);
1325 GetItem()->Freeze(unfrozen_callback.Get());
1326 GetItem()->MediaSessionInfoChanged(nullptr);
1327 GetItem()->MediaSessionMetadataChanged(base::nullopt);
1328 GetItem()->MediaControllerImageChanged(
1329 media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap());
1330
1331 // The item should be frozen and the view should contain the old data.
1332 EXPECT_TRUE(GetItem()->frozen());
1333 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1334 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1335 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1336 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1337 EXPECT_FALSE(GetArtworkImage().isNull());
1338
1339 // Bind the item to a new controller that's playing instead of paused.
1340 auto new_media_controller = std::make_unique<TestMediaController>();
1341 media_session::mojom::MediaSessionInfoPtr session_info(
1342 media_session::mojom::MediaSessionInfo::New());
1343 session_info->playback_state =
1344 media_session::mojom::MediaPlaybackState::kPlaying;
1345 session_info->is_controllable = true;
1346 GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
1347 session_info.Clone());
1348
1349 // The item will receive a MediaSessionInfoChanged.
1350 GetItem()->MediaSessionInfoChanged(session_info.Clone());
1351
1352 // The item should still be frozen, and the view should contain the old data.
1353 EXPECT_TRUE(GetItem()->frozen());
1354 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1355 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1356 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1357 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1358 EXPECT_FALSE(GetArtworkImage().isNull());
1359
1360 // Update the metadata.
1361 media_session::MediaMetadata metadata;
1362 metadata.title = base::ASCIIToUTF16("title2");
1363 metadata.artist = base::ASCIIToUTF16("artist2");
1364 GetItem()->MediaSessionMetadataChanged(metadata);
1365
1366 // The item should still be frozen, and waiting for a new image.
1367 EXPECT_TRUE(GetItem()->frozen());
1368 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1369 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
1370 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
1371 EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
1372 EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
1373 EXPECT_FALSE(GetArtworkImage().isNull());
1374
1375 // Once we receive artwork, the item should unfreeze.
1376 EXPECT_CALL(unfrozen_callback, Run);
1377 SkBitmap new_image;
1378 new_image.allocN32Pixels(10, 10);
1379 new_image.eraseColor(SK_ColorYELLOW);
1380 GetItem()->MediaControllerImageChanged(
1381 media_session::mojom::MediaSessionImageType::kArtwork, new_image);
1382
1383 EXPECT_FALSE(GetItem()->frozen());
1384 testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
1385 EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
1386 EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
1387 EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
1388 EXPECT_EQ(base::ASCIIToUTF16("artist2"), artist_label()->GetText());
1389 EXPECT_FALSE(GetArtworkImage().isNull());
1390 }
1391
TEST_F(MAYBE_MediaNotificationViewImplTest,ForcedExpandedState)1392 TEST_F(MAYBE_MediaNotificationViewImplTest, ForcedExpandedState) {
1393 // Make the view expandable.
1394 EnableAllActions();
1395
1396 // Force it to be expanded.
1397 bool expanded_state = true;
1398 view()->SetForcedExpandedState(&expanded_state);
1399 EXPECT_TRUE(IsActuallyExpanded());
1400
1401 // Since it's forced, clicking on the header should not toggle the expanded
1402 // state.
1403 SimulateHeaderClick();
1404 EXPECT_TRUE(IsActuallyExpanded());
1405
1406 // Force it to be not expanded.
1407 expanded_state = false;
1408 view()->SetForcedExpandedState(&expanded_state);
1409 EXPECT_FALSE(IsActuallyExpanded());
1410
1411 // Since it's forced, clicking on the header should not toggle the expanded
1412 // state.
1413 SimulateHeaderClick();
1414 EXPECT_FALSE(IsActuallyExpanded());
1415
1416 // Stop forcing expanded state.
1417 view()->SetForcedExpandedState(nullptr);
1418 EXPECT_FALSE(IsActuallyExpanded());
1419
1420 // Clicking on the header should toggle the expanded state.
1421 SimulateHeaderClick();
1422 EXPECT_TRUE(IsActuallyExpanded());
1423 }
1424
TEST_F(MAYBE_MediaNotificationViewImplTest,AllowsHidingOfAppIcon)1425 TEST_F(MAYBE_MediaNotificationViewImplTest, AllowsHidingOfAppIcon) {
1426 MediaNotificationViewImpl shows_icon(&container(), nullptr, nullptr,
1427 base::string16(), kViewWidth,
1428 /*should_show_icon=*/true);
1429 MediaNotificationViewImpl hides_icon(&container(), nullptr, nullptr,
1430 base::string16(), kViewWidth,
1431 /*should_show_icon=*/false);
1432
1433 EXPECT_TRUE(
1434 GetHeaderRow(&shows_icon)->app_icon_view_for_testing()->GetVisible());
1435 EXPECT_FALSE(
1436 GetHeaderRow(&hides_icon)->app_icon_view_for_testing()->GetVisible());
1437 }
1438
TEST_F(MAYBE_MediaNotificationViewImplTest,ClickHeader_NotifyContainer)1439 TEST_F(MAYBE_MediaNotificationViewImplTest, ClickHeader_NotifyContainer) {
1440 EXPECT_CALL(container(), OnHeaderClicked());
1441 SimulateHeaderClick();
1442 }
1443
1444 } // namespace media_message_center
1445