1 // Copyright 2019 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 "chrome/browser/ui/global_media_controls/cast_media_notification_provider.h"
6 
7 #include "chrome/test/base/testing_profile.h"
8 #include "components/media_message_center/media_notification_controller.h"
9 #include "components/media_message_center/media_notification_view.h"
10 #include "components/media_router/browser/test/mock_media_router.h"
11 #include "components/media_router/common/media_route.h"
12 #include "content/public/test/browser_task_environment.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/vector_icon_types.h"
16 
17 using media_router::MediaRoute;
18 using media_router::RouteControllerType;
19 using testing::_;
20 
21 namespace {
22 
CreateRoute(const std::string & route_id,const std::string & source_id="source_id")23 MediaRoute CreateRoute(const std::string& route_id,
24                        const std::string& source_id = "source_id") {
25   media_router::MediaRoute route(route_id, media_router::MediaSource(source_id),
26                                  "sink_id", "description", true, true);
27   route.set_controller_type(media_router::RouteControllerType::kGeneric);
28   return route;
29 }
30 
31 class MockMediaNotificationController
32     : public media_message_center::MediaNotificationController {
33  public:
34   MockMediaNotificationController() = default;
35   ~MockMediaNotificationController() = default;
36 
37   MOCK_METHOD(void, ShowNotification, (const std::string& id));
38   MOCK_METHOD(void, HideNotification, (const std::string& id));
39   MOCK_METHOD(void, RemoveItem, (const std::string& id));
GetTaskRunner() const40   scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override {
41     return nullptr;
42   }
43   MOCK_METHOD(void,
44               LogMediaSessionActionButtonPressed,
45               (const std::string& id,
46                media_session::mojom::MediaSessionAction action));
47 };
48 
49 class MockMediaNotificationView
50     : public media_message_center::MediaNotificationView {
51  public:
52   MOCK_METHOD1(SetExpanded, void(bool));
53   MOCK_METHOD2(UpdateCornerRadius, void(int, int));
54   MOCK_METHOD1(SetForcedExpandedState, void(bool*));
55   MOCK_METHOD1(UpdateWithMediaSessionInfo,
56                void(const media_session::mojom::MediaSessionInfoPtr&));
57   MOCK_METHOD1(UpdateWithMediaMetadata,
58                void(const media_session::MediaMetadata&));
59   MOCK_METHOD1(
60       UpdateWithMediaActions,
61       void(const base::flat_set<media_session::mojom::MediaSessionAction>&));
62   MOCK_METHOD1(UpdateWithMediaArtwork, void(const gfx::ImageSkia&));
63   MOCK_METHOD1(UpdateWithFavicon, void(const gfx::ImageSkia&));
64   MOCK_METHOD1(UpdateWithVectorIcon, void(const gfx::VectorIcon& vector_icon));
65   MOCK_METHOD1(UpdateDeviceSelectorAvailability, void(bool availability));
66 };
67 
68 class MockClosure {
69  public:
70   MOCK_METHOD0(Run, void());
71 };
72 
73 }  // namespace
74 
75 class CastMediaNotificationProviderTest : public testing::Test {
76  public:
SetUp()77   void SetUp() override {
78     notification_provider_ = std::make_unique<CastMediaNotificationProvider>(
79         &profile_, &router_, &notification_controller_,
80         base::BindRepeating(&MockClosure::Run,
81                             base::Unretained(&items_changed_callback_)));
82   }
83 
TearDown()84   void TearDown() override { notification_provider_.reset(); }
85 
86  protected:
87   content::BrowserTaskEnvironment task_environment_;
88   TestingProfile profile_;
89   std::unique_ptr<CastMediaNotificationProvider> notification_provider_;
90   MockMediaNotificationController notification_controller_;
91   media_router::MockMediaRouter router_;
92   MockClosure items_changed_callback_;
93 };
94 
TEST_F(CastMediaNotificationProviderTest,AddAndRemoveRoute)95 TEST_F(CastMediaNotificationProviderTest, AddAndRemoveRoute) {
96   const std::string route_id = "route-id-1";
97   MediaRoute route = CreateRoute(route_id);
98 
99   EXPECT_CALL(items_changed_callback_, Run());
100   notification_provider_->OnRoutesUpdated({route}, {});
101   testing::Mock::VerifyAndClearExpectations(&items_changed_callback_);
102   EXPECT_TRUE(notification_provider_->HasItems());
103   EXPECT_NE(nullptr, notification_provider_->GetNotificationItem(route_id));
104 
105   EXPECT_CALL(items_changed_callback_, Run());
106   notification_provider_->OnRoutesUpdated({}, {});
107   testing::Mock::VerifyAndClearExpectations(&items_changed_callback_);
108   EXPECT_FALSE(notification_provider_->HasItems());
109 }
110 
TEST_F(CastMediaNotificationProviderTest,UpdateRoute)111 TEST_F(CastMediaNotificationProviderTest, UpdateRoute) {
112   const std::string route_id = "route-id-1";
113   MediaRoute route = CreateRoute(route_id);
114 
115   notification_provider_->OnRoutesUpdated({route}, {});
116   auto* item = static_cast<CastMediaNotificationItem*>(
117       notification_provider_->GetNotificationItem(route_id).get());
118   MockMediaNotificationView view;
119   item->SetView(&view);
120 
121   const std::string new_sink = "new sink";
122   const std::string new_description = "new description";
123   route.set_media_sink_name(new_sink);
124   route.set_description(new_description);
125 
126   EXPECT_CALL(view, UpdateWithMediaMetadata(_))
127       .WillOnce([&](const media_session::MediaMetadata& metadata) {
128         const std::string separator = " \xC2\xB7 ";
129         EXPECT_EQ(base::UTF8ToUTF16(new_description + separator + new_sink),
130                   metadata.source_title);
131       });
132   notification_provider_->OnRoutesUpdated({route}, {});
133 }
134 
TEST_F(CastMediaNotificationProviderTest,RoutesWithoutNotifications)135 TEST_F(CastMediaNotificationProviderTest, RoutesWithoutNotifications) {
136   // These routes should not have notification items created for them.
137   MediaRoute non_display_route = CreateRoute("route-1");
138   non_display_route.set_for_display(false);
139   MediaRoute no_controller_route = CreateRoute("route-2");
140   no_controller_route.set_controller_type(RouteControllerType::kNone);
141   MediaRoute multizone_member_route = CreateRoute("route-3", "cast:705D30C6");
142 
143   notification_provider_->OnRoutesUpdated(
144       {non_display_route, no_controller_route, multizone_member_route}, {});
145   EXPECT_FALSE(notification_provider_->HasItems());
146 }
147