1 // Copyright 2015 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.
8 #include "base/bind_helpers.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/run_loop.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "chrome/browser/media/router/media_route.h"
13 #include "chrome/browser/media/router/media_router_mojo_test.h"
14 #include "chrome/browser/media/router/mock_media_router.h"
15 #include "chrome/browser/media/router/test_helper.h"
16 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/process_manager.h"
20 #include "extensions/browser/process_manager_factory.h"
21 #include "media/base/gmock_callback_support.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
27 using testing::Invoke
;
28 using testing::Pointee
;
29 using testing::Return
;
30 using testing::ReturnRef
;
31 using testing::SaveArg
;
33 namespace media_router
{
37 const char kDescription
[] = "description";
38 const char kError
[] = "error";
39 const char kExtensionId
[] = "extension1234";
40 const char kMessage
[] = "message";
41 const char kSource
[] = "source1";
42 const char kSource2
[] = "source2";
43 const char kRouteId
[] = "routeId";
44 const char kRouteId2
[] = "routeId2";
45 const char kSink
[] = "sink";
46 const char kSink2
[] = "sink2";
47 const char kSinkName
[] = "sinkName";
51 // Adapts Invoke(), which takes a move-only scoped_ptr parameter (not mockable)
52 // to a variant that accepts raw pointers instead (mock friendly).
53 class RouteResponseCallbackHandler
{
55 void Invoke(scoped_ptr
<MediaRoute
> route
, const std::string
& error_text
) {
56 InvokeObserver(route
.get(), error_text
);
59 MOCK_METHOD2(InvokeObserver
,
60 void(MediaRoute
* route
, const std::string
& error_text
));
63 class SendMessageCallbackHandler
{
65 MOCK_METHOD1(Invoke
, void(bool));
69 void StoreAndRun(T
* result
, const base::Closure
& closure
, const T
& result_val
) {
74 class MediaRouterMojoImplTest
: public MediaRouterMojoTest
{
76 MediaRouterMojoImplTest() {}
77 ~MediaRouterMojoImplTest() override
{}
80 // ProcessManager with a mocked method subset, for testing extension suspend
82 class TestProcessManager
: public extensions::ProcessManager
{
84 explicit TestProcessManager(content::BrowserContext
* context
)
85 : extensions::ProcessManager(
88 extensions::ExtensionRegistry::Get(context
)) {}
89 ~TestProcessManager() override
{}
91 static scoped_ptr
<KeyedService
> Create(content::BrowserContext
* context
) {
92 return make_scoped_ptr(new TestProcessManager(context
));
95 MOCK_METHOD1(IsEventPageSuspended
, bool(const std::string
& ext_id
));
97 MOCK_METHOD2(WakeEventPage
,
98 bool(const std::string
& extension_id
,
99 const base::Callback
<void(bool)>& callback
));
102 DISALLOW_COPY_AND_ASSIGN(TestProcessManager
);
105 // Mockable class for awaiting ProvideMediaRouter callbacks.
106 class ProvideMediaRouterHandler
{
108 MOCK_METHOD1(Invoke
, void(const std::string
& instance_id
));
111 TEST_F(MediaRouterMojoImplTest
, CreateRoute
) {
112 MediaRoute
expected_route(kRouteId
, MediaSource(std::string(kSource
)),
113 MediaSink(kSink
, kSinkName
), "", false);
114 interfaces::MediaRoutePtr route
= interfaces::MediaRoute::New();
115 route
->media_source
= kSource
;
116 route
->media_sink
= interfaces::MediaSink::New();
117 route
->media_sink
->sink_id
= kSink
;
118 route
->media_sink
->name
= kSinkName
;
119 route
->media_route_id
= kRouteId
;
120 route
->description
= kDescription
;
122 // Use a lambda function as an invocation target here to work around
123 // a limitation with GMock::Invoke that prevents it from using move-only types
124 // in runnable parameter lists.
125 EXPECT_CALL(mock_mojo_media_router_service_
,
126 CreateRoute(mojo::String(kSource
), mojo::String(kSink
), _
))
128 [&route
](const mojo::String
& source
, const mojo::String
& sink
,
129 const interfaces::MediaRouter::CreateRouteCallback
& cb
) {
130 cb
.Run(route
.Pass(), mojo::String());
133 RouteResponseCallbackHandler handler
;
134 EXPECT_CALL(handler
, InvokeObserver(Pointee(Equals(expected_route
)), ""));
135 router()->CreateRoute(kSource
, kSink
,
136 base::Bind(&RouteResponseCallbackHandler::Invoke
,
137 base::Unretained(&handler
)));
141 TEST_F(MediaRouterMojoImplTest
, CreateRouteFails
) {
142 EXPECT_CALL(mock_mojo_media_router_service_
,
143 CreateRoute(mojo::String(kSource
), mojo::String(kSink
), _
))
145 Invoke([](const mojo::String
& source
, const mojo::String
& sink
,
146 const interfaces::MediaRouter::CreateRouteCallback
& cb
) {
147 cb
.Run(interfaces::MediaRoutePtr(), mojo::String(kError
));
150 RouteResponseCallbackHandler handler
;
151 EXPECT_CALL(handler
, InvokeObserver(nullptr, kError
));
152 router()->CreateRoute(kSource
, kSink
,
153 base::Bind(&RouteResponseCallbackHandler::Invoke
,
154 base::Unretained(&handler
)));
158 TEST_F(MediaRouterMojoImplTest
, CloseRoute
) {
159 EXPECT_CALL(mock_mojo_media_router_service_
,
160 CloseRoute(mojo::String(kRouteId
)));
161 router()->CloseRoute(kRouteId
);
165 TEST_F(MediaRouterMojoImplTest
, RegisterAndUnregisterMediaSinksObserver
) {
166 MediaSource
media_source(kSource
);
168 MockMediaRouter mock_router
;
169 EXPECT_CALL(mock_mojo_media_router_service_
,
170 StartObservingMediaSinks(mojo::String(kSource
))).Times(2);
171 EXPECT_CALL(mock_mojo_media_router_service_
,
172 StartObservingMediaSinks(mojo::String(kSource2
)));
174 MediaSinksObserver
* captured_observer
;
175 EXPECT_CALL(mock_router
, RegisterMediaSinksObserver(_
))
177 .WillRepeatedly(SaveArg
<0>(&captured_observer
));
179 MockMediaSinksObserver
sinks_observer(&mock_router
, media_source
);
180 EXPECT_EQ(&sinks_observer
, captured_observer
);
181 router()->RegisterMediaSinksObserver(&sinks_observer
);
182 MockMediaSinksObserver
extra_sinks_observer(&mock_router
, media_source
);
183 EXPECT_EQ(&extra_sinks_observer
, captured_observer
);
184 router()->RegisterMediaSinksObserver(&extra_sinks_observer
);
185 MockMediaSinksObserver
unrelated_sinks_observer(&mock_router
,
186 MediaSource(kSource2
));
187 EXPECT_EQ(&unrelated_sinks_observer
, captured_observer
);
188 router()->RegisterMediaSinksObserver(&unrelated_sinks_observer
);
190 std::vector
<MediaSink
> expected_sinks
;
191 expected_sinks
.push_back(MediaSink(kSink
, kSinkName
));
192 expected_sinks
.push_back(MediaSink(kSink2
, kSinkName
));
194 mojo::Array
<interfaces::MediaSinkPtr
> mojo_sinks(2);
195 mojo_sinks
[0] = interfaces::MediaSink::New();
196 mojo_sinks
[0]->sink_id
= kSink
;
197 mojo_sinks
[0]->name
= kSink
;
198 mojo_sinks
[1] = interfaces::MediaSink::New();
199 mojo_sinks
[1]->sink_id
= kSink2
;
200 mojo_sinks
[1]->name
= kSink2
;
202 EXPECT_CALL(sinks_observer
, OnSinksReceived(SequenceEquals(expected_sinks
)));
203 EXPECT_CALL(extra_sinks_observer
,
204 OnSinksReceived(SequenceEquals(expected_sinks
)));
205 mojo_media_router_observer_
->OnSinksReceived(media_source
.id(),
209 EXPECT_CALL(mock_router
, UnregisterMediaSinksObserver(&sinks_observer
));
210 EXPECT_CALL(mock_router
, UnregisterMediaSinksObserver(&extra_sinks_observer
));
211 EXPECT_CALL(mock_router
,
212 UnregisterMediaSinksObserver(&unrelated_sinks_observer
));
213 EXPECT_CALL(mock_mojo_media_router_service_
,
214 StopObservingMediaSinks(mojo::String(kSource
)));
215 EXPECT_CALL(mock_mojo_media_router_service_
,
216 StopObservingMediaSinks(mojo::String(kSource2
)));
217 router()->UnregisterMediaSinksObserver(&sinks_observer
);
218 router()->UnregisterMediaSinksObserver(&extra_sinks_observer
);
219 router()->UnregisterMediaSinksObserver(&unrelated_sinks_observer
);
223 TEST_F(MediaRouterMojoImplTest
, RegisterAndUnregisterMediaRoutesObserver
) {
224 MockMediaRouter mock_router
;
225 EXPECT_CALL(mock_mojo_media_router_service_
, StartObservingMediaRoutes())
228 MediaRoutesObserver
* observer_captured
;
229 EXPECT_CALL(mock_router
, RegisterMediaRoutesObserver(_
))
231 .WillRepeatedly(SaveArg
<0>(&observer_captured
));
232 MockMediaRoutesObserver
routes_observer(&mock_router
);
233 EXPECT_EQ(observer_captured
, &routes_observer
);
234 MockMediaRoutesObserver
extra_routes_observer(&mock_router
);
235 EXPECT_EQ(observer_captured
, &extra_routes_observer
);
236 router()->RegisterMediaRoutesObserver(&routes_observer
);
237 router()->RegisterMediaRoutesObserver(&extra_routes_observer
);
239 std::vector
<MediaRoute
> expected_routes
;
240 expected_routes
.push_back(MediaRoute(kRouteId
, MediaSource(kSource
),
241 MediaSink(kSink
, kSink
), kDescription
,
243 expected_routes
.push_back(MediaRoute(kRouteId2
, MediaSource(kSource
),
244 MediaSink(kSink
, kSink
), kDescription
,
247 mojo::Array
<interfaces::MediaRoutePtr
> mojo_routes(2);
248 mojo_routes
[0] = interfaces::MediaRoute::New();
249 mojo_routes
[0]->media_route_id
= kRouteId
;
250 mojo_routes
[0]->media_source
= kSource
;
251 mojo_routes
[0]->media_sink
= interfaces::MediaSink::New();
252 mojo_routes
[0]->media_sink
->sink_id
= kSink
;
253 mojo_routes
[0]->media_sink
->name
= kSink
;
254 mojo_routes
[0]->description
= kDescription
;
255 mojo_routes
[0]->is_local
= false;
256 mojo_routes
[1] = interfaces::MediaRoute::New();
257 mojo_routes
[1]->media_route_id
= kRouteId2
;
258 mojo_routes
[1]->media_source
= kSource
;
259 mojo_routes
[1]->media_sink
= interfaces::MediaSink::New();
260 mojo_routes
[1]->media_sink
->sink_id
= kSink
;
261 mojo_routes
[1]->media_sink
->name
= kSink
;
262 mojo_routes
[1]->description
= kDescription
;
263 mojo_routes
[1]->is_local
= false;
265 EXPECT_CALL(routes_observer
,
266 OnRoutesUpdated(SequenceEquals(expected_routes
)));
267 EXPECT_CALL(extra_routes_observer
,
268 OnRoutesUpdated(SequenceEquals(expected_routes
)));
269 mojo_media_router_observer_
->OnRoutesUpdated(mojo_routes
.Pass());
272 EXPECT_CALL(mock_router
, UnregisterMediaRoutesObserver(&routes_observer
));
273 EXPECT_CALL(mock_router
,
274 UnregisterMediaRoutesObserver(&extra_routes_observer
));
275 router()->UnregisterMediaRoutesObserver(&routes_observer
);
276 router()->UnregisterMediaRoutesObserver(&extra_routes_observer
);
277 EXPECT_CALL(mock_mojo_media_router_service_
, StopObservingMediaRoutes());
281 TEST_F(MediaRouterMojoImplTest
, SendRouteMessage
) {
283 mock_mojo_media_router_service_
,
284 SendRouteMessage(mojo::String(kRouteId
), mojo::String(kMessage
), _
))
286 [](const MediaRoute::Id
& route_id
, const std::string
& message
,
287 const interfaces::MediaRouter::SendRouteMessageCallback
& cb
) {
291 SendMessageCallbackHandler handler
;
292 EXPECT_CALL(handler
, Invoke(true));
293 router()->SendRouteMessage(kRouteId
, kMessage
,
294 base::Bind(&SendMessageCallbackHandler::Invoke
,
295 base::Unretained(&handler
)));
299 TEST_F(MediaRouterMojoImplTest
, QueuedWhileAsleep
) {
300 EXPECT_CALL(mock_event_page_tracker_
, IsEventPageSuspended(extension_id()))
302 .WillRepeatedly(Return(true));
303 EXPECT_CALL(mock_event_page_tracker_
, WakeEventPage(extension_id(), _
))
305 .WillRepeatedly(Return(true));
306 router()->CloseRoute(kRouteId
);
307 router()->CloseRoute(kRouteId2
);
309 EXPECT_CALL(mock_event_page_tracker_
, IsEventPageSuspended(extension_id()))
311 .WillRepeatedly(Return(false));
312 EXPECT_CALL(mock_mojo_media_router_service_
,
313 CloseRoute(mojo::String(kRouteId
)));
314 EXPECT_CALL(mock_mojo_media_router_service_
,
315 CloseRoute(mojo::String(kRouteId2
)));
316 ConnectProviderManagerService();
320 // Temporarily disabled until the issues with extension system teardown
322 // TODO(kmarshall): Re-enable this test. (http://crbug.com/490468)
323 TEST(MediaRouterMojoExtensionTest
, DISABLED_DeferredBindingAndSuspension
) {
324 base::MessageLoop
message_loop(mojo::common::MessagePumpMojo::Create());
326 // Set up a mock ProcessManager instance.
327 TestingProfile profile
;
328 extensions::ProcessManagerFactory::GetInstance()->SetTestingFactory(
329 &profile
, &TestProcessManager::Create
);
330 TestProcessManager
* process_manager
= static_cast<TestProcessManager
*>(
331 extensions::ProcessManager::Get(&profile
));
333 // Create MR and its proxy, so that it can be accessed through Mojo.
334 MediaRouterMojoImpl
media_router(process_manager
);
335 interfaces::MediaRouterObserverPtr mojo_media_router_observer
;
337 // Create a client object and its Mojo proxy.
338 testing::StrictMock
<MockMojoMediaRouterService
>
339 mock_mojo_media_router_service
;
340 interfaces::MediaRouterPtr mojo_media_router
;
342 // CloseRoute is called before *any* extension has connected.
343 // It should be queued.
344 media_router
.CloseRoute(kRouteId
);
346 // Construct bindings so that |media_router| delegates calls to
347 // |mojo_media_router|, which are then handled by
348 // |mock_mojo_media_router_service|.
349 scoped_ptr
<mojo::Binding
<interfaces::MediaRouter
>> binding(
350 new mojo::Binding
<interfaces::MediaRouter
>(
351 &mock_mojo_media_router_service
, mojo::GetProxy(&mojo_media_router
)));
352 media_router
.BindToMojoRequest(mojo::GetProxy(&mojo_media_router_observer
),
355 // |mojo_media_router| signals its readiness to the MR by registering
356 // itself via ProvideMediaRouter().
357 // Now that the |media_router| and |mojo_media_router| are fully initialized,
358 // the queued CloseRoute() call should be executed.
359 ProvideMediaRouterHandler provide_handler
;
360 EXPECT_CALL(provide_handler
, Invoke(testing::Not("")));
361 EXPECT_CALL(*process_manager
, IsEventPageSuspended(kExtensionId
))
362 .WillOnce(Return(false));
363 EXPECT_CALL(mock_mojo_media_router_service
,
364 CloseRoute(mojo::String(kRouteId
)));
365 mojo_media_router_observer
->ProvideMediaRouter(
366 mojo_media_router
.Pass(), base::Bind(&ProvideMediaRouterHandler::Invoke
,
367 base::Unretained(&provide_handler
)));
368 message_loop
.RunUntilIdle();
370 // Extension is suspended and re-awoken.
372 media_router
.BindToMojoRequest(mojo::GetProxy(&mojo_media_router_observer
),
374 EXPECT_CALL(*process_manager
, IsEventPageSuspended(kExtensionId
))
375 .WillOnce(Return(true));
376 EXPECT_CALL(*process_manager
, WakeEventPage(kExtensionId
, _
))
377 .WillOnce(testing::DoAll(media::RunCallback
<1>(true), Return(true)));
378 media_router
.CloseRoute(kRouteId2
);
379 message_loop
.RunUntilIdle();
381 // ProvideMediaRouter() is called.
382 // The queued CloseRoute(kRouteId2) call should be executed.
383 EXPECT_CALL(provide_handler
, Invoke(testing::Not("")));
384 EXPECT_CALL(*process_manager
, IsEventPageSuspended(kExtensionId
))
385 .WillOnce(Return(false));
386 EXPECT_CALL(mock_mojo_media_router_service
,
387 CloseRoute(mojo::String(kRouteId2
)));
388 binding
.reset(new mojo::Binding
<interfaces::MediaRouter
>(
389 &mock_mojo_media_router_service
, mojo::GetProxy(&mojo_media_router
)));
390 mojo_media_router_observer
->ProvideMediaRouter(
391 mojo_media_router
.Pass(), base::Bind(&ProvideMediaRouterHandler::Invoke
,
392 base::Unretained(&provide_handler
)));
393 message_loop
.RunUntilIdle();
396 } // namespace media_router