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.
5 #include "base/memory/scoped_ptr.h"
6 #include "base/message_loop/message_loop.h"
7 #include "base/run_loop.h"
8 #include "content/browser/presentation/presentation_service_impl.h"
9 #include "content/public/browser/presentation_service_delegate.h"
10 #include "content/public/browser/presentation_session.h"
11 #include "content/test/test_render_frame_host.h"
12 #include "content/test/test_render_view_host.h"
13 #include "content/test/test_web_contents.h"
14 #include "mojo/public/cpp/bindings/interface_ptr.h"
15 #include "testing/gmock/include/gmock/gmock.h"
19 using ::testing::InvokeWithoutArgs
;
20 using ::testing::Mock
;
21 using ::testing::Return
;
22 using ::testing::SaveArg
;
26 class MockPresentationServiceDelegate
: public PresentationServiceDelegate
{
28 MOCK_METHOD1(AddObserver
,
29 void(PresentationServiceDelegate::Observer
* observer
));
30 MOCK_METHOD1(RemoveObserver
,
31 void(PresentationServiceDelegate::Observer
* observer
));
32 MOCK_METHOD3(AddScreenAvailabilityListener
,
34 int render_process_id
,
36 PresentationScreenAvailabilityListener
* listener
));
37 MOCK_METHOD3(RemoveScreenAvailabilityListener
,
39 int render_process_id
,
41 PresentationScreenAvailabilityListener
* listener
));
44 int render_process_id
,
46 MOCK_METHOD4(SetDefaultPresentationUrl
,
48 int render_process_id
,
50 const std::string
& default_presentation_url
,
51 const std::string
& default_presentation_id
));
52 MOCK_METHOD6(StartSession
,
54 int render_process_id
,
56 const std::string
& presentation_url
,
57 const std::string
& presentation_id
,
58 const PresentationSessionSuccessCallback
& success_cb
,
59 const PresentationSessionErrorCallback
& error_cb
));
60 MOCK_METHOD6(JoinSession
,
62 int render_process_id
,
64 const std::string
& presentation_url
,
65 const std::string
& presentation_id
,
66 const PresentationSessionSuccessCallback
& success_cb
,
67 const PresentationSessionErrorCallback
& error_cb
));
70 class PresentationServiceImplTest
: public RenderViewHostImplTestHarness
{
72 PresentationServiceImplTest() : callback_count_(0) {}
74 void SetUp() override
{
75 RenderViewHostImplTestHarness::SetUp();
77 EXPECT_CALL(mock_delegate_
, AddObserver(_
)).Times(1);
78 service_impl_
.reset(mojo::WeakBindToProxy(
79 new PresentationServiceImpl(
80 contents()->GetMainFrame(), contents(), &mock_delegate_
),
84 void TearDown() override
{
87 EXPECT_CALL(mock_delegate_
, RemoveObserver(Eq(service_impl_
.get())))
89 service_impl_
.reset();
91 RenderViewHostImplTestHarness::TearDown();
94 void ListenForScreenAvailabilityAndWait(
95 const std::string
& presentation_url
,
96 const base::Callback
<void(const std::string
&, bool)>& callback
,
97 bool delegate_success
) {
98 base::RunLoop run_loop
;
99 // This will call to |service_impl_| via mojo. Process the message
101 // The callback shouldn't be invoked since there is no availability
103 EXPECT_CALL(mock_delegate_
, AddScreenAvailabilityListener(_
, _
, _
))
105 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
106 Return(delegate_success
)));
107 service_ptr_
->ListenForScreenAvailability(presentation_url
, callback
);
110 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_
));
113 void ExpectListenerDoesNotExist(const std::string
& presentation_url
) {
114 const auto& contexts
= service_impl_
->availability_contexts_
;
115 auto it
= contexts
.find(presentation_url
);
116 EXPECT_TRUE(it
== contexts
.end());
119 void RunLoopFor(base::TimeDelta duration
) {
120 base::RunLoop run_loop
;
121 base::MessageLoop::current()->PostDelayedTask(
122 FROM_HERE
, run_loop
.QuitClosure(), duration
);
126 void SaveQuitClosureAndRunLoop() {
127 base::RunLoop run_loop
;
128 run_loop_quit_closure_
= run_loop
.QuitClosure();
130 run_loop_quit_closure_
.Reset();
133 void ShouldNotBeCalled(const std::string
& presentation_url
, bool available
) {
134 FAIL() << "Callback unexpectedly invoked with "
135 << "url = " << presentation_url
<< ", available = " << available
;
138 void SimulateScreenAvailabilityChange(
139 const std::string
& presentation_url
, bool available
) {
140 const auto& contexts
= service_impl_
->availability_contexts_
;
141 auto it
= contexts
.find(presentation_url
);
142 ASSERT_TRUE(it
!= contexts
.end());
143 it
->second
->OnScreenAvailabilityChanged(available
);
146 void ScreenAvailabilityChangedCallback(
148 const std::string
& presentation_url
,
151 EXPECT_EQ(expected
, available
);
152 if (!run_loop_quit_closure_
.is_null())
153 run_loop_quit_closure_
.Run();
157 EXPECT_CALL(mock_delegate_
, Reset(_
, _
))
161 void ExpectCleanState() {
162 EXPECT_TRUE(service_impl_
->availability_contexts_
.empty());
163 EXPECT_TRUE(service_impl_
->default_presentation_url_
.empty());
164 EXPECT_TRUE(service_impl_
->default_presentation_id_
.empty());
165 EXPECT_TRUE(service_impl_
->queued_start_session_requests_
.empty());
168 void ExpectNewSessionMojoCallbackSuccess(
169 presentation::PresentationSessionInfoPtr info
,
170 presentation::PresentationErrorPtr error
) {
171 EXPECT_FALSE(info
.is_null());
172 EXPECT_TRUE(error
.is_null());
173 if (!run_loop_quit_closure_
.is_null())
174 run_loop_quit_closure_
.Run();
177 void ExpectNewSessionMojoCallbackError(
178 presentation::PresentationSessionInfoPtr info
,
179 presentation::PresentationErrorPtr error
) {
180 EXPECT_TRUE(info
.is_null());
181 EXPECT_FALSE(error
.is_null());
182 if (!run_loop_quit_closure_
.is_null())
183 run_loop_quit_closure_
.Run();
186 MockPresentationServiceDelegate mock_delegate_
;
187 scoped_ptr
<PresentationServiceImpl
> service_impl_
;
188 mojo::InterfacePtr
<presentation::PresentationService
> service_ptr_
;
189 base::Closure run_loop_quit_closure_
;
193 TEST_F(PresentationServiceImplTest
, ListenForScreenAvailability
) {
194 std::string
presentation_url("http://fooUrl");
195 ListenForScreenAvailabilityAndWait(
198 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
199 base::Unretained(this), true),
202 // Different presentation URL.
203 ListenForScreenAvailabilityAndWait(
205 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
206 base::Unretained(this)),
209 // Result now available; callback will be invoked with availability result.
210 SimulateScreenAvailabilityChange(presentation_url
, true);
211 SaveQuitClosureAndRunLoop();
213 EXPECT_EQ(1, callback_count_
);
215 // Result updated but callback not invoked since it's been erased.
216 SimulateScreenAvailabilityChange(presentation_url
, false);
218 // Register another callback which should immediately invoke callback
219 // since updated result is available.
220 service_ptr_
->ListenForScreenAvailability(
223 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
224 base::Unretained(this),
226 SaveQuitClosureAndRunLoop();
227 EXPECT_EQ(2, callback_count_
);
230 TEST_F(PresentationServiceImplTest
, Reset
) {
231 std::string
presentation_url("http://fooUrl");
232 ListenForScreenAvailabilityAndWait(
234 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
235 base::Unretained(this)),
239 service_impl_
->Reset();
242 EXPECT_EQ(0, callback_count_
);
245 TEST_F(PresentationServiceImplTest
, DidNavigateThisFrame
) {
246 std::string
presentation_url("http://fooUrl");
247 ListenForScreenAvailabilityAndWait(
249 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
250 base::Unretained(this)),
254 service_impl_
->DidNavigateAnyFrame(
255 contents()->GetMainFrame(),
256 content::LoadCommittedDetails(),
257 content::FrameNavigateParams());
261 TEST_F(PresentationServiceImplTest
, DidNavigateNotThisFrame
) {
262 std::string
presentation_url("http://fooUrl");
263 ListenForScreenAvailabilityAndWait(
266 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
267 base::Unretained(this),
271 // TODO(imcheng): How to get a different RenderFrameHost?
272 service_impl_
->DidNavigateAnyFrame(
274 content::LoadCommittedDetails(),
275 content::FrameNavigateParams());
277 // Availability is reported and callback is invoked since it was not
279 SimulateScreenAvailabilityChange(presentation_url
, true);
280 SaveQuitClosureAndRunLoop();
281 EXPECT_EQ(1, callback_count_
);
284 TEST_F(PresentationServiceImplTest
, ThisRenderFrameDeleted
) {
285 std::string
presentation_url("http://fooUrl");
286 ListenForScreenAvailabilityAndWait(
288 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
289 base::Unretained(this)),
293 service_impl_
->RenderFrameDeleted(contents()->GetMainFrame());
297 TEST_F(PresentationServiceImplTest
, NotThisRenderFrameDeleted
) {
298 std::string
presentation_url("http://fooUrl");
299 ListenForScreenAvailabilityAndWait(
302 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
303 base::Unretained(this),
307 // TODO(imcheng): How to get a different RenderFrameHost?
308 service_impl_
->RenderFrameDeleted(nullptr);
310 // Availability is reported and callback should be invoked since listener
311 // has not been deleted.
312 SimulateScreenAvailabilityChange(presentation_url
, true);
313 SaveQuitClosureAndRunLoop();
314 EXPECT_EQ(1, callback_count_
);
317 TEST_F(PresentationServiceImplTest
, ListenForScreenAvailabilityTwice
) {
318 std::string
presentation_url("http://fooUrl");
319 ListenForScreenAvailabilityAndWait(
322 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
323 base::Unretained(this),
327 // Second call should overwrite the callback from first call.
328 // It shouldn't result in an extra call to delegate.
329 service_ptr_
->ListenForScreenAvailability(
332 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
333 base::Unretained(this),
336 // Cannot use ListenForScreenAvailabilityAndWait here since the mock delegate
337 // won't be triggered again to quit the RunLoop.
338 RunLoopFor(base::TimeDelta::FromMilliseconds(50));
340 // Result now available; callback will be invoked with availability result.
341 SimulateScreenAvailabilityChange(presentation_url
, false);
342 SaveQuitClosureAndRunLoop();
344 EXPECT_EQ(2, callback_count_
);
347 TEST_F(PresentationServiceImplTest
, DelegateFails
) {
348 std::string
presentation_url("http://fooUrl");
349 ListenForScreenAvailabilityAndWait(
351 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
352 base::Unretained(this)),
355 ExpectListenerDoesNotExist(presentation_url
);
358 TEST_F(PresentationServiceImplTest
, SetDefaultPresentationUrl
) {
359 std::string
url1("http://fooUrl");
360 std::string
dpu_id("dpuId");
361 EXPECT_CALL(mock_delegate_
,
362 SetDefaultPresentationUrl(_
, _
, Eq(url1
), Eq(dpu_id
)))
364 service_impl_
->SetDefaultPresentationURL(url1
, dpu_id
);
365 EXPECT_EQ(url1
, service_impl_
->default_presentation_url_
);
367 // Now there should be a callback registered with the DPU.
368 ListenForScreenAvailabilityAndWait(
371 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
372 base::Unretained(this), true),
374 const auto& contexts
= service_impl_
->availability_contexts_
;
375 auto it
= contexts
.find(url1
);
376 ASSERT_TRUE(it
!= contexts
.end());
377 EXPECT_TRUE(it
->second
->HasPendingCallbacks());
379 std::string
url2("http://barUrl");
380 // Sets different DPU.
381 // Adds listener for url2 and removes listener for url1.
382 // Also, the callback from url1 is transferred to url2.
385 AddScreenAvailabilityListener(_
, _
, _
))
386 .WillOnce(Return(true));
389 RemoveScreenAvailabilityListener(_
, _
, _
))
391 EXPECT_CALL(mock_delegate_
,
392 SetDefaultPresentationUrl(_
, _
, Eq(url2
), Eq(dpu_id
)))
394 service_impl_
->SetDefaultPresentationURL(url2
, dpu_id
);
395 EXPECT_EQ(url2
, service_impl_
->default_presentation_url_
);
397 it
= contexts
.find(url2
);
398 ASSERT_TRUE(it
!= contexts
.end());
399 EXPECT_TRUE(it
->second
->HasPendingCallbacks());
402 TEST_F(PresentationServiceImplTest
, SetSameDefaultPresentationUrl
) {
403 std::string
url("http://fooUrl");
404 std::string
dpu_id("dpuId");
405 EXPECT_CALL(mock_delegate_
,
406 SetDefaultPresentationUrl(_
, _
, Eq(url
), Eq(dpu_id
)))
408 service_impl_
->SetDefaultPresentationURL(url
, dpu_id
);
409 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_
));
410 EXPECT_EQ(url
, service_impl_
->default_presentation_url_
);
412 // Same URL as before; no-ops.
413 service_impl_
->SetDefaultPresentationURL(url
, dpu_id
);
414 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_
));
415 EXPECT_EQ(url
, service_impl_
->default_presentation_url_
);
418 TEST_F(PresentationServiceImplTest
, ClearDefaultPresentationUrl
) {
419 std::string
url("http://fooUrl");
420 std::string
dpu_id("dpuId");
421 EXPECT_CALL(mock_delegate_
,
422 SetDefaultPresentationUrl(_
, _
, Eq(url
), Eq(dpu_id
)))
424 service_impl_
->SetDefaultPresentationURL(url
, dpu_id
);
425 EXPECT_EQ(url
, service_impl_
->default_presentation_url_
);
427 // Now there should be a callback registered with the DPU.
428 ListenForScreenAvailabilityAndWait(
431 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
432 base::Unretained(this), true),
435 const auto& contexts
= service_impl_
->availability_contexts_
;
436 auto it
= contexts
.find(url
);
437 ASSERT_TRUE(it
!= contexts
.end());
438 EXPECT_TRUE(it
->second
->HasPendingCallbacks());
440 // Clears the default presentation URL. Transfers the listener from url to
444 AddScreenAvailabilityListener(_
, _
, _
))
445 .WillOnce(Return(true));
448 RemoveScreenAvailabilityListener(_
, _
, _
))
452 SetDefaultPresentationUrl(_
, _
, Eq(std::string()), Eq(std::string())))
454 service_impl_
->SetDefaultPresentationURL(std::string(), std::string());
455 EXPECT_TRUE(service_impl_
->default_presentation_url_
.empty());
457 it
= contexts
.find(url
);
458 ASSERT_TRUE(it
== contexts
.end());
461 TEST_F(PresentationServiceImplTest
, StartSessionSuccess
) {
462 std::string
presentation_url("http://fooUrl");
463 std::string
presentation_id("presentationId");
464 service_ptr_
->StartSession(
468 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
469 base::Unretained(this)));
470 base::RunLoop run_loop
;
471 base::Callback
<void(const PresentationSessionInfo
&)> success_cb
;
472 EXPECT_CALL(mock_delegate_
, StartSession(
473 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
475 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
476 SaveArg
<4>(&success_cb
)));
478 success_cb
.Run(PresentationSessionInfo(presentation_url
, presentation_id
));
479 SaveQuitClosureAndRunLoop();
482 TEST_F(PresentationServiceImplTest
, StartSessionError
) {
483 std::string
presentation_url("http://fooUrl");
484 std::string
presentation_id("presentationId");
485 service_ptr_
->StartSession(
489 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackError
,
490 base::Unretained(this)));
491 base::RunLoop run_loop
;
492 base::Callback
<void(const PresentationError
&)> error_cb
;
493 EXPECT_CALL(mock_delegate_
, StartSession(
494 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
496 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
497 SaveArg
<5>(&error_cb
)));
499 error_cb
.Run(PresentationError(PRESENTATION_ERROR_UNKNOWN
, "Error message"));
500 SaveQuitClosureAndRunLoop();
503 TEST_F(PresentationServiceImplTest
, JoinSessionSuccess
) {
504 std::string
presentation_url("http://fooUrl");
505 std::string
presentation_id("presentationId");
506 service_ptr_
->JoinSession(
510 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
511 base::Unretained(this)));
512 base::RunLoop run_loop
;
513 base::Callback
<void(const PresentationSessionInfo
&)> success_cb
;
514 EXPECT_CALL(mock_delegate_
, JoinSession(
515 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
517 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
518 SaveArg
<4>(&success_cb
)));
520 success_cb
.Run(PresentationSessionInfo(presentation_url
, presentation_id
));
521 SaveQuitClosureAndRunLoop();
524 TEST_F(PresentationServiceImplTest
, JoinSessionError
) {
525 std::string
presentation_url("http://fooUrl");
526 std::string
presentation_id("presentationId");
527 service_ptr_
->JoinSession(
531 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackError
,
532 base::Unretained(this)));
533 base::RunLoop run_loop
;
534 base::Callback
<void(const PresentationError
&)> error_cb
;
535 EXPECT_CALL(mock_delegate_
, JoinSession(
536 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
538 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
539 SaveArg
<5>(&error_cb
)));
541 error_cb
.Run(PresentationError(PRESENTATION_ERROR_UNKNOWN
, "Error message"));
542 SaveQuitClosureAndRunLoop();
545 TEST_F(PresentationServiceImplTest
, StartSessionInProgress
) {
546 std::string
presentation_url1("http://fooUrl");
547 std::string
presentation_id1("presentationId1");
548 std::string
presentation_url2("http://barUrl");
549 std::string
presentation_id2("presentationId2");
550 service_ptr_
->StartSession(
554 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
555 base::Unretained(this)));
556 service_ptr_
->StartSession(
560 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
561 base::Unretained(this)));
562 base::RunLoop run_loop
;
563 base::Callback
<void(const PresentationSessionInfo
&)> success_cb
;
564 EXPECT_CALL(mock_delegate_
, StartSession(
565 _
, _
, Eq(presentation_url1
), Eq(presentation_id1
), _
, _
))
567 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
568 SaveArg
<4>(&success_cb
)));
571 // Running the callback means the first request is done. It should now
572 // move on to the queued request.
573 EXPECT_CALL(mock_delegate_
, StartSession(
574 _
, _
, Eq(presentation_url2
), Eq(presentation_id2
), _
, _
))
576 success_cb
.Run(PresentationSessionInfo(presentation_url1
, presentation_id1
));
577 SaveQuitClosureAndRunLoop();
580 } // namespace content