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 auto request
= mojo::GetProxy(&service_ptr_
);
78 EXPECT_CALL(mock_delegate_
, AddObserver(_
)).Times(1);
79 service_impl_
.reset(new PresentationServiceImpl(
80 contents()->GetMainFrame(), contents(), &mock_delegate_
));
81 service_impl_
->Bind(request
.Pass());
84 void TearDown() override
{
86 if (service_impl_
.get()) {
87 EXPECT_CALL(mock_delegate_
, RemoveObserver(Eq(service_impl_
.get())))
89 service_impl_
.reset();
92 RenderViewHostImplTestHarness::TearDown();
95 void ListenForScreenAvailabilityAndWait(
96 const std::string
& presentation_url
,
97 const base::Callback
<void(const std::string
&, bool)>& callback
,
98 bool delegate_success
) {
99 base::RunLoop run_loop
;
100 // This will call to |service_impl_| via mojo. Process the message
102 // The callback shouldn't be invoked since there is no availability
104 EXPECT_CALL(mock_delegate_
, AddScreenAvailabilityListener(_
, _
, _
))
106 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
107 Return(delegate_success
)));
108 service_ptr_
->ListenForScreenAvailability(presentation_url
, callback
);
111 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_
));
114 void ExpectListenerDoesNotExist(const std::string
& presentation_url
) {
115 const auto& contexts
= service_impl_
->availability_contexts_
;
116 auto it
= contexts
.find(presentation_url
);
117 EXPECT_TRUE(it
== contexts
.end());
120 void RunLoopFor(base::TimeDelta duration
) {
121 base::RunLoop run_loop
;
122 base::MessageLoop::current()->PostDelayedTask(
123 FROM_HERE
, run_loop
.QuitClosure(), duration
);
127 void SaveQuitClosureAndRunLoop() {
128 base::RunLoop run_loop
;
129 run_loop_quit_closure_
= run_loop
.QuitClosure();
131 run_loop_quit_closure_
.Reset();
134 void ShouldNotBeCalled(const std::string
& presentation_url
, bool available
) {
135 FAIL() << "Callback unexpectedly invoked with "
136 << "url = " << presentation_url
<< ", available = " << available
;
139 void SimulateScreenAvailabilityChange(
140 const std::string
& presentation_url
, bool available
) {
141 const auto& contexts
= service_impl_
->availability_contexts_
;
142 auto it
= contexts
.find(presentation_url
);
143 ASSERT_TRUE(it
!= contexts
.end());
144 it
->second
->OnScreenAvailabilityChanged(available
);
147 void ScreenAvailabilityChangedCallback(
149 const std::string
& presentation_url
,
152 EXPECT_EQ(expected
, available
);
153 if (!run_loop_quit_closure_
.is_null())
154 run_loop_quit_closure_
.Run();
158 EXPECT_CALL(mock_delegate_
, Reset(_
, _
))
162 void ExpectCleanState() {
163 EXPECT_TRUE(service_impl_
->availability_contexts_
.empty());
164 EXPECT_TRUE(service_impl_
->default_presentation_url_
.empty());
165 EXPECT_TRUE(service_impl_
->default_presentation_id_
.empty());
166 EXPECT_TRUE(service_impl_
->queued_start_session_requests_
.empty());
169 void ExpectNewSessionMojoCallbackSuccess(
170 presentation::PresentationSessionInfoPtr info
,
171 presentation::PresentationErrorPtr error
) {
172 EXPECT_FALSE(info
.is_null());
173 EXPECT_TRUE(error
.is_null());
174 if (!run_loop_quit_closure_
.is_null())
175 run_loop_quit_closure_
.Run();
178 void ExpectNewSessionMojoCallbackError(
179 presentation::PresentationSessionInfoPtr info
,
180 presentation::PresentationErrorPtr error
) {
181 EXPECT_TRUE(info
.is_null());
182 EXPECT_FALSE(error
.is_null());
183 if (!run_loop_quit_closure_
.is_null())
184 run_loop_quit_closure_
.Run();
187 MockPresentationServiceDelegate mock_delegate_
;
188 scoped_ptr
<PresentationServiceImpl
> service_impl_
;
189 mojo::InterfacePtr
<presentation::PresentationService
> service_ptr_
;
190 base::Closure run_loop_quit_closure_
;
194 TEST_F(PresentationServiceImplTest
, ListenForScreenAvailability
) {
195 std::string
presentation_url("http://fooUrl");
196 ListenForScreenAvailabilityAndWait(
199 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
200 base::Unretained(this), true),
203 // Different presentation URL.
204 ListenForScreenAvailabilityAndWait(
206 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
207 base::Unretained(this)),
210 // Result now available; callback will be invoked with availability result.
211 SimulateScreenAvailabilityChange(presentation_url
, true);
212 SaveQuitClosureAndRunLoop();
214 EXPECT_EQ(1, callback_count_
);
216 // Result updated but callback not invoked since it's been erased.
217 SimulateScreenAvailabilityChange(presentation_url
, false);
219 // Register another callback which should immediately invoke callback
220 // since updated result is available.
221 service_ptr_
->ListenForScreenAvailability(
224 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
225 base::Unretained(this),
227 SaveQuitClosureAndRunLoop();
228 EXPECT_EQ(2, callback_count_
);
231 TEST_F(PresentationServiceImplTest
, Reset
) {
232 std::string
presentation_url("http://fooUrl");
233 ListenForScreenAvailabilityAndWait(
235 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
236 base::Unretained(this)),
240 service_impl_
->Reset();
243 EXPECT_EQ(0, callback_count_
);
246 TEST_F(PresentationServiceImplTest
, DidNavigateThisFrame
) {
247 std::string
presentation_url("http://fooUrl");
248 ListenForScreenAvailabilityAndWait(
250 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
251 base::Unretained(this)),
255 service_impl_
->DidNavigateAnyFrame(
256 contents()->GetMainFrame(),
257 content::LoadCommittedDetails(),
258 content::FrameNavigateParams());
262 TEST_F(PresentationServiceImplTest
, DidNavigateNotThisFrame
) {
263 std::string
presentation_url("http://fooUrl");
264 ListenForScreenAvailabilityAndWait(
267 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
268 base::Unretained(this),
272 // TODO(imcheng): How to get a different RenderFrameHost?
273 service_impl_
->DidNavigateAnyFrame(
275 content::LoadCommittedDetails(),
276 content::FrameNavigateParams());
278 // Availability is reported and callback is invoked since it was not
280 SimulateScreenAvailabilityChange(presentation_url
, true);
281 SaveQuitClosureAndRunLoop();
282 EXPECT_EQ(1, callback_count_
);
285 TEST_F(PresentationServiceImplTest
, ThisRenderFrameDeleted
) {
286 std::string
presentation_url("http://fooUrl");
287 ListenForScreenAvailabilityAndWait(
289 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
290 base::Unretained(this)),
295 // Since the frame matched the service, |service_impl_| will be deleted.
296 PresentationServiceImpl
* service
= service_impl_
.release();
297 EXPECT_CALL(mock_delegate_
, RemoveObserver(Eq(service
))).Times(1);
298 service
->RenderFrameDeleted(contents()->GetMainFrame());
301 TEST_F(PresentationServiceImplTest
, NotThisRenderFrameDeleted
) {
302 std::string
presentation_url("http://fooUrl");
303 ListenForScreenAvailabilityAndWait(
306 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
307 base::Unretained(this),
311 // TODO(imcheng): How to get a different RenderFrameHost?
312 service_impl_
->RenderFrameDeleted(nullptr);
314 // Availability is reported and callback should be invoked since listener
315 // has not been deleted.
316 SimulateScreenAvailabilityChange(presentation_url
, true);
317 SaveQuitClosureAndRunLoop();
318 EXPECT_EQ(1, callback_count_
);
321 TEST_F(PresentationServiceImplTest
, ListenForScreenAvailabilityTwice
) {
322 std::string
presentation_url("http://fooUrl");
323 ListenForScreenAvailabilityAndWait(
326 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
327 base::Unretained(this),
331 // Second call should overwrite the callback from first call.
332 // It shouldn't result in an extra call to delegate.
333 service_ptr_
->ListenForScreenAvailability(
336 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
337 base::Unretained(this),
340 // Cannot use ListenForScreenAvailabilityAndWait here since the mock delegate
341 // won't be triggered again to quit the RunLoop.
342 RunLoopFor(base::TimeDelta::FromMilliseconds(50));
344 // Result now available; callback will be invoked with availability result.
345 SimulateScreenAvailabilityChange(presentation_url
, false);
346 SaveQuitClosureAndRunLoop();
348 EXPECT_EQ(2, callback_count_
);
351 TEST_F(PresentationServiceImplTest
, DelegateFails
) {
352 std::string
presentation_url("http://fooUrl");
353 ListenForScreenAvailabilityAndWait(
355 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled
,
356 base::Unretained(this)),
359 ExpectListenerDoesNotExist(presentation_url
);
362 TEST_F(PresentationServiceImplTest
, SetDefaultPresentationUrl
) {
363 std::string
url1("http://fooUrl");
364 std::string
dpu_id("dpuId");
365 EXPECT_CALL(mock_delegate_
,
366 SetDefaultPresentationUrl(_
, _
, Eq(url1
), Eq(dpu_id
)))
368 service_impl_
->SetDefaultPresentationURL(url1
, dpu_id
);
369 EXPECT_EQ(url1
, service_impl_
->default_presentation_url_
);
371 // Now there should be a callback registered with the DPU.
372 ListenForScreenAvailabilityAndWait(
375 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
376 base::Unretained(this), true),
378 const auto& contexts
= service_impl_
->availability_contexts_
;
379 auto it
= contexts
.find(url1
);
380 ASSERT_TRUE(it
!= contexts
.end());
381 EXPECT_TRUE(it
->second
->HasPendingCallbacks());
383 std::string
url2("http://barUrl");
384 // Sets different DPU.
385 // Adds listener for url2 and removes listener for url1.
386 // Also, the callback from url1 is transferred to url2.
389 AddScreenAvailabilityListener(_
, _
, _
))
390 .WillOnce(Return(true));
393 RemoveScreenAvailabilityListener(_
, _
, _
))
395 EXPECT_CALL(mock_delegate_
,
396 SetDefaultPresentationUrl(_
, _
, Eq(url2
), Eq(dpu_id
)))
398 service_impl_
->SetDefaultPresentationURL(url2
, dpu_id
);
399 EXPECT_EQ(url2
, service_impl_
->default_presentation_url_
);
401 it
= contexts
.find(url2
);
402 ASSERT_TRUE(it
!= contexts
.end());
403 EXPECT_TRUE(it
->second
->HasPendingCallbacks());
406 TEST_F(PresentationServiceImplTest
, SetSameDefaultPresentationUrl
) {
407 std::string
url("http://fooUrl");
408 std::string
dpu_id("dpuId");
409 EXPECT_CALL(mock_delegate_
,
410 SetDefaultPresentationUrl(_
, _
, Eq(url
), Eq(dpu_id
)))
412 service_impl_
->SetDefaultPresentationURL(url
, dpu_id
);
413 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_
));
414 EXPECT_EQ(url
, service_impl_
->default_presentation_url_
);
416 // Same URL as before; no-ops.
417 service_impl_
->SetDefaultPresentationURL(url
, dpu_id
);
418 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_delegate_
));
419 EXPECT_EQ(url
, service_impl_
->default_presentation_url_
);
422 TEST_F(PresentationServiceImplTest
, ClearDefaultPresentationUrl
) {
423 std::string
url("http://fooUrl");
424 std::string
dpu_id("dpuId");
425 EXPECT_CALL(mock_delegate_
,
426 SetDefaultPresentationUrl(_
, _
, Eq(url
), Eq(dpu_id
)))
428 service_impl_
->SetDefaultPresentationURL(url
, dpu_id
);
429 EXPECT_EQ(url
, service_impl_
->default_presentation_url_
);
431 // Now there should be a callback registered with the DPU.
432 ListenForScreenAvailabilityAndWait(
435 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback
,
436 base::Unretained(this), true),
439 const auto& contexts
= service_impl_
->availability_contexts_
;
440 auto it
= contexts
.find(url
);
441 ASSERT_TRUE(it
!= contexts
.end());
442 EXPECT_TRUE(it
->second
->HasPendingCallbacks());
444 // Clears the default presentation URL. Transfers the listener from url to
448 AddScreenAvailabilityListener(_
, _
, _
))
449 .WillOnce(Return(true));
452 RemoveScreenAvailabilityListener(_
, _
, _
))
456 SetDefaultPresentationUrl(_
, _
, Eq(std::string()), Eq(std::string())))
458 service_impl_
->SetDefaultPresentationURL(std::string(), std::string());
459 EXPECT_TRUE(service_impl_
->default_presentation_url_
.empty());
461 it
= contexts
.find(url
);
462 ASSERT_TRUE(it
== contexts
.end());
465 TEST_F(PresentationServiceImplTest
, StartSessionSuccess
) {
466 std::string
presentation_url("http://fooUrl");
467 std::string
presentation_id("presentationId");
468 service_ptr_
->StartSession(
472 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
473 base::Unretained(this)));
474 base::RunLoop run_loop
;
475 base::Callback
<void(const PresentationSessionInfo
&)> success_cb
;
476 EXPECT_CALL(mock_delegate_
, StartSession(
477 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
479 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
480 SaveArg
<4>(&success_cb
)));
482 success_cb
.Run(PresentationSessionInfo(presentation_url
, presentation_id
));
483 SaveQuitClosureAndRunLoop();
486 TEST_F(PresentationServiceImplTest
, StartSessionError
) {
487 std::string
presentation_url("http://fooUrl");
488 std::string
presentation_id("presentationId");
489 service_ptr_
->StartSession(
493 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackError
,
494 base::Unretained(this)));
495 base::RunLoop run_loop
;
496 base::Callback
<void(const PresentationError
&)> error_cb
;
497 EXPECT_CALL(mock_delegate_
, StartSession(
498 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
500 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
501 SaveArg
<5>(&error_cb
)));
503 error_cb
.Run(PresentationError(PRESENTATION_ERROR_UNKNOWN
, "Error message"));
504 SaveQuitClosureAndRunLoop();
507 TEST_F(PresentationServiceImplTest
, JoinSessionSuccess
) {
508 std::string
presentation_url("http://fooUrl");
509 std::string
presentation_id("presentationId");
510 service_ptr_
->JoinSession(
514 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
515 base::Unretained(this)));
516 base::RunLoop run_loop
;
517 base::Callback
<void(const PresentationSessionInfo
&)> success_cb
;
518 EXPECT_CALL(mock_delegate_
, JoinSession(
519 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
521 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
522 SaveArg
<4>(&success_cb
)));
524 success_cb
.Run(PresentationSessionInfo(presentation_url
, presentation_id
));
525 SaveQuitClosureAndRunLoop();
528 TEST_F(PresentationServiceImplTest
, JoinSessionError
) {
529 std::string
presentation_url("http://fooUrl");
530 std::string
presentation_id("presentationId");
531 service_ptr_
->JoinSession(
535 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackError
,
536 base::Unretained(this)));
537 base::RunLoop run_loop
;
538 base::Callback
<void(const PresentationError
&)> error_cb
;
539 EXPECT_CALL(mock_delegate_
, JoinSession(
540 _
, _
, Eq(presentation_url
), Eq(presentation_id
), _
, _
))
542 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
543 SaveArg
<5>(&error_cb
)));
545 error_cb
.Run(PresentationError(PRESENTATION_ERROR_UNKNOWN
, "Error message"));
546 SaveQuitClosureAndRunLoop();
549 TEST_F(PresentationServiceImplTest
, StartSessionInProgress
) {
550 std::string
presentation_url1("http://fooUrl");
551 std::string
presentation_id1("presentationId1");
552 std::string
presentation_url2("http://barUrl");
553 std::string
presentation_id2("presentationId2");
554 service_ptr_
->StartSession(
558 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
559 base::Unretained(this)));
560 service_ptr_
->StartSession(
564 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess
,
565 base::Unretained(this)));
566 base::RunLoop run_loop
;
567 base::Callback
<void(const PresentationSessionInfo
&)> success_cb
;
568 EXPECT_CALL(mock_delegate_
, StartSession(
569 _
, _
, Eq(presentation_url1
), Eq(presentation_id1
), _
, _
))
571 InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
),
572 SaveArg
<4>(&success_cb
)));
575 // Running the callback means the first request is done. It should now
576 // move on to the queued request.
577 EXPECT_CALL(mock_delegate_
, StartSession(
578 _
, _
, Eq(presentation_url2
), Eq(presentation_id2
), _
, _
))
580 success_cb
.Run(PresentationSessionInfo(presentation_url1
, presentation_id1
));
581 SaveQuitClosureAndRunLoop();
584 } // namespace content