[Presentation API] Fix stability issues with PresentationService.
[chromium-blink-merge.git] / content / browser / presentation / presentation_service_impl_unittest.cc
blob1f7f758405395f55c0f519d7aeba2e52f4302d3e
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"
17 using ::testing::_;
18 using ::testing::Eq;
19 using ::testing::InvokeWithoutArgs;
20 using ::testing::Mock;
21 using ::testing::Return;
22 using ::testing::SaveArg;
24 namespace content {
26 class MockPresentationServiceDelegate : public PresentationServiceDelegate {
27 public:
28 MOCK_METHOD1(AddObserver,
29 void(PresentationServiceDelegate::Observer* observer));
30 MOCK_METHOD1(RemoveObserver,
31 void(PresentationServiceDelegate::Observer* observer));
32 MOCK_METHOD3(AddScreenAvailabilityListener,
33 bool(
34 int render_process_id,
35 int routing_id,
36 PresentationScreenAvailabilityListener* listener));
37 MOCK_METHOD3(RemoveScreenAvailabilityListener,
38 void(
39 int render_process_id,
40 int routing_id,
41 PresentationScreenAvailabilityListener* listener));
42 MOCK_METHOD2(Reset,
43 void(
44 int render_process_id,
45 int routing_id));
46 MOCK_METHOD4(SetDefaultPresentationUrl,
47 void(
48 int render_process_id,
49 int routing_id,
50 const std::string& default_presentation_url,
51 const std::string& default_presentation_id));
52 MOCK_METHOD6(StartSession,
53 void(
54 int render_process_id,
55 int render_frame_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,
61 void(
62 int render_process_id,
63 int render_frame_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 {
71 public:
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 {
85 service_ptr_.reset();
86 if (service_impl_.get()) {
87 EXPECT_CALL(mock_delegate_, RemoveObserver(Eq(service_impl_.get())))
88 .Times(1);
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
101 // using RunLoop.
102 // The callback shouldn't be invoked since there is no availability
103 // result yet.
104 EXPECT_CALL(mock_delegate_, AddScreenAvailabilityListener(_, _, _))
105 .WillOnce(DoAll(
106 InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
107 Return(delegate_success)));
108 service_ptr_->ListenForScreenAvailability(presentation_url, callback);
109 run_loop.Run();
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);
124 run_loop.Run();
127 void SaveQuitClosureAndRunLoop() {
128 base::RunLoop run_loop;
129 run_loop_quit_closure_ = run_loop.QuitClosure();
130 run_loop.Run();
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(
148 bool expected,
149 const std::string& presentation_url,
150 bool available) {
151 ++callback_count_;
152 EXPECT_EQ(expected, available);
153 if (!run_loop_quit_closure_.is_null())
154 run_loop_quit_closure_.Run();
157 void ExpectReset() {
158 EXPECT_CALL(mock_delegate_, Reset(_, _))
159 .Times(1);
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_;
191 int callback_count_;
194 TEST_F(PresentationServiceImplTest, ListenForScreenAvailability) {
195 std::string presentation_url("http://fooUrl");
196 ListenForScreenAvailabilityAndWait(
197 presentation_url,
198 base::Bind(
199 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
200 base::Unretained(this), true),
201 true);
203 // Different presentation URL.
204 ListenForScreenAvailabilityAndWait(
205 "http://barUrl",
206 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
207 base::Unretained(this)),
208 true);
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(
222 presentation_url,
223 base::Bind(
224 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
225 base::Unretained(this),
226 false));
227 SaveQuitClosureAndRunLoop();
228 EXPECT_EQ(2, callback_count_);
231 TEST_F(PresentationServiceImplTest, Reset) {
232 std::string presentation_url("http://fooUrl");
233 ListenForScreenAvailabilityAndWait(
234 presentation_url,
235 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
236 base::Unretained(this)),
237 true);
239 ExpectReset();
240 service_impl_->Reset();
241 ExpectCleanState();
243 EXPECT_EQ(0, callback_count_);
246 TEST_F(PresentationServiceImplTest, DidNavigateThisFrame) {
247 std::string presentation_url("http://fooUrl");
248 ListenForScreenAvailabilityAndWait(
249 presentation_url,
250 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
251 base::Unretained(this)),
252 true);
254 ExpectReset();
255 service_impl_->DidNavigateAnyFrame(
256 contents()->GetMainFrame(),
257 content::LoadCommittedDetails(),
258 content::FrameNavigateParams());
259 ExpectCleanState();
262 TEST_F(PresentationServiceImplTest, DidNavigateNotThisFrame) {
263 std::string presentation_url("http://fooUrl");
264 ListenForScreenAvailabilityAndWait(
265 presentation_url,
266 base::Bind(
267 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
268 base::Unretained(this),
269 true),
270 true);
272 // TODO(imcheng): How to get a different RenderFrameHost?
273 service_impl_->DidNavigateAnyFrame(
274 nullptr,
275 content::LoadCommittedDetails(),
276 content::FrameNavigateParams());
278 // Availability is reported and callback is invoked since it was not
279 // removed.
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(
288 presentation_url,
289 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
290 base::Unretained(this)),
291 true);
293 ExpectReset();
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(
304 presentation_url,
305 base::Bind(
306 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
307 base::Unretained(this),
308 true),
309 true);
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(
324 presentation_url,
325 base::Bind(
326 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
327 base::Unretained(this),
328 false),
329 true);
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(
334 presentation_url,
335 base::Bind(
336 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
337 base::Unretained(this),
338 false));
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(
354 presentation_url,
355 base::Bind(&PresentationServiceImplTest::ShouldNotBeCalled,
356 base::Unretained(this)),
357 false);
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)))
367 .Times(1);
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(
373 url1,
374 base::Bind(
375 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
376 base::Unretained(this), true),
377 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.
387 EXPECT_CALL(
388 mock_delegate_,
389 AddScreenAvailabilityListener(_, _, _))
390 .WillOnce(Return(true));
391 EXPECT_CALL(
392 mock_delegate_,
393 RemoveScreenAvailabilityListener(_, _, _))
394 .Times(1);
395 EXPECT_CALL(mock_delegate_,
396 SetDefaultPresentationUrl(_, _, Eq(url2), Eq(dpu_id)))
397 .Times(1);
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)))
411 .Times(1);
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)))
427 .Times(1);
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(
433 url,
434 base::Bind(
435 &PresentationServiceImplTest::ScreenAvailabilityChangedCallback,
436 base::Unretained(this), true),
437 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
445 // "1-UA" mode.
446 EXPECT_CALL(
447 mock_delegate_,
448 AddScreenAvailabilityListener(_, _, _))
449 .WillOnce(Return(true));
450 EXPECT_CALL(
451 mock_delegate_,
452 RemoveScreenAvailabilityListener(_, _, _))
453 .Times(1);
454 EXPECT_CALL(
455 mock_delegate_,
456 SetDefaultPresentationUrl(_, _, Eq(std::string()), Eq(std::string())))
457 .Times(1);
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(
469 presentation_url,
470 presentation_id,
471 base::Bind(
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), _, _))
478 .WillOnce(DoAll(
479 InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
480 SaveArg<4>(&success_cb)));
481 run_loop.Run();
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(
490 presentation_url,
491 presentation_id,
492 base::Bind(
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), _, _))
499 .WillOnce(DoAll(
500 InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
501 SaveArg<5>(&error_cb)));
502 run_loop.Run();
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(
511 presentation_url,
512 presentation_id,
513 base::Bind(
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), _, _))
520 .WillOnce(DoAll(
521 InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
522 SaveArg<4>(&success_cb)));
523 run_loop.Run();
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(
532 presentation_url,
533 presentation_id,
534 base::Bind(
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), _, _))
541 .WillOnce(DoAll(
542 InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
543 SaveArg<5>(&error_cb)));
544 run_loop.Run();
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(
555 presentation_url1,
556 presentation_id1,
557 base::Bind(
558 &PresentationServiceImplTest::ExpectNewSessionMojoCallbackSuccess,
559 base::Unretained(this)));
560 service_ptr_->StartSession(
561 presentation_url2,
562 presentation_id2,
563 base::Bind(
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), _, _))
570 .WillOnce(DoAll(
571 InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit),
572 SaveArg<4>(&success_cb)));
573 run_loop.Run();
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), _, _))
579 .Times(1);
580 success_cb.Run(PresentationSessionInfo(presentation_url1, presentation_id1));
581 SaveQuitClosureAndRunLoop();
584 } // namespace content