Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: chrome/browser/media/router/media_router_mojo_impl_unittest.cc

Issue 1826403002: [Media Router] Moves mojo-specific code into mojo/ folder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include <stddef.h>
6 #include <stdint.h>
7 #include <string>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/macros.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/run_loop.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/test/histogram_tester.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "chrome/browser/media/router/issue.h"
20 #include "chrome/browser/media/router/media_route.h"
21 #include "chrome/browser/media/router/media_router_metrics.h"
22 #include "chrome/browser/media/router/media_router_mojo_test.h"
23 #include "chrome/browser/media/router/media_router_type_converters.h"
24 #include "chrome/browser/media/router/mock_media_router.h"
25 #include "chrome/browser/media/router/presentation_session_messages_observer.h"
26 #include "chrome/browser/media/router/test_helper.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_browser_process.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "components/version_info/version_info.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/process_manager.h"
33 #include "extensions/browser/process_manager_factory.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/extension_builder.h"
36 #include "extensions/common/test_util.h"
37 #include "extensions/common/value_builder.h"
38 #include "media/base/gmock_callback_support.h"
39 #include "mojo/message_pump/message_pump_mojo.h"
40 #include "testing/gmock/include/gmock/gmock.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42
43 using testing::_;
44 using testing::Eq;
45 using testing::Invoke;
46 using testing::InvokeWithoutArgs;
47 using testing::IsEmpty;
48 using testing::Mock;
49 using testing::Not;
50 using testing::Pointee;
51 using testing::Return;
52 using testing::ReturnRef;
53 using testing::SaveArg;
54
55 namespace media_router {
56
57 using PresentationConnectionState =
58 interfaces::MediaRouter::PresentationConnectionState;
59 using PresentationConnectionCloseReason =
60 interfaces::MediaRouter::PresentationConnectionCloseReason;
61
62 namespace {
63
64 const char kDescription[] = "description";
65 const char kError[] = "error";
66 const char kMessage[] = "message";
67 const char kSource[] = "source1";
68 const char kSource2[] = "source2";
69 const char kRouteId[] = "routeId";
70 const char kRouteId2[] = "routeId2";
71 const char kJoinableRouteId[] = "joinableRouteId";
72 const char kJoinableRouteId2[] = "joinableRouteId2";
73 const char kSinkId[] = "sink";
74 const char kSinkId2[] = "sink2";
75 const char kSinkName[] = "sinkName";
76 const char kPresentationId[] = "presentationId";
77 const char kOrigin[] = "http://origin/";
78 const int kInvalidTabId = -1;
79 const uint8_t kBinaryMessage[] = {0x01, 0x02, 0x03, 0x04};
80 const int kTimeoutMillis = 5 * 1000;
81
82 bool ArePresentationSessionMessagesEqual(
83 const content::PresentationSessionMessage* expected,
84 const content::PresentationSessionMessage* actual) {
85 if (expected->type != actual->type)
86 return false;
87
88 return expected->is_binary() ? *expected->data == *actual->data
89 : expected->message == actual->message;
90 }
91
92 interfaces::IssuePtr CreateMojoIssue(const std::string& title) {
93 interfaces::IssuePtr mojoIssue = interfaces::Issue::New();
94 mojoIssue->title = title;
95 mojoIssue->message = "msg";
96 mojoIssue->route_id = "";
97 mojoIssue->default_action = interfaces::Issue::ActionType::DISMISS;
98 mojoIssue->secondary_actions =
99 mojo::Array<interfaces::Issue::ActionType>::New(0);
100 mojoIssue->severity = interfaces::Issue::Severity::WARNING;
101 mojoIssue->is_blocking = false;
102 mojoIssue->help_url = "";
103 return mojoIssue;
104 }
105
106 } // namespace
107
108 class RouteResponseCallbackHandler {
109 public:
110 void Invoke(const RouteRequestResult& result) {
111 DoInvoke(result.route(), result.presentation_id(), result.error(),
112 result.result_code());
113 }
114 MOCK_METHOD4(DoInvoke,
115 void(const MediaRoute* route,
116 const std::string& presentation_id,
117 const std::string& error_text,
118 RouteRequestResult::ResultCode result_code));
119 };
120
121 class SendMessageCallbackHandler {
122 public:
123 MOCK_METHOD1(Invoke, void(bool));
124 };
125
126 class ListenForMessagesCallbackHandler {
127 public:
128 ListenForMessagesCallbackHandler(
129 ScopedVector<content::PresentationSessionMessage> expected_messages,
130 bool pass_ownership)
131 : expected_messages_(std::move(expected_messages)),
132 pass_ownership_(pass_ownership) {}
133 void Invoke(const ScopedVector<content::PresentationSessionMessage>& messages,
134 bool pass_ownership) {
135 InvokeObserver();
136 EXPECT_EQ(pass_ownership_, pass_ownership);
137 EXPECT_EQ(messages.size(), expected_messages_.size());
138 for (size_t i = 0; i < expected_messages_.size(); ++i) {
139 EXPECT_TRUE(ArePresentationSessionMessagesEqual(expected_messages_[i],
140 messages[i]));
141 }
142 }
143
144 MOCK_METHOD0(InvokeObserver, void());
145
146 private:
147 ScopedVector<content::PresentationSessionMessage> expected_messages_;
148 bool pass_ownership_;
149 };
150
151 template <typename T>
152 void StoreAndRun(T* result, const base::Closure& closure, const T& result_val) {
153 *result = result_val;
154 closure.Run();
155 }
156
157 class MediaRouterMojoImplTest : public MediaRouterMojoTest {
158 public:
159 MediaRouterMojoImplTest() {}
160 ~MediaRouterMojoImplTest() override {}
161 };
162
163 // ProcessManager with a mocked method subset, for testing extension suspend
164 // handling.
165 class TestProcessManager : public extensions::ProcessManager {
166 public:
167 explicit TestProcessManager(content::BrowserContext* context)
168 : extensions::ProcessManager(
169 context,
170 context,
171 extensions::ExtensionRegistry::Get(context)) {}
172 ~TestProcessManager() override {}
173
174 static scoped_ptr<KeyedService> Create(content::BrowserContext* context) {
175 return make_scoped_ptr(new TestProcessManager(context));
176 }
177
178 MOCK_METHOD1(IsEventPageSuspended, bool(const std::string& ext_id));
179
180 MOCK_METHOD2(WakeEventPage,
181 bool(const std::string& extension_id,
182 const base::Callback<void(bool)>& callback));
183
184 private:
185 DISALLOW_COPY_AND_ASSIGN(TestProcessManager);
186 };
187
188 // Mockable class for awaiting RegisterMediaRouteProvider callbacks.
189 class RegisterMediaRouteProviderHandler {
190 public:
191 MOCK_METHOD1(Invoke, void(const std::string& instance_id));
192 };
193
194 TEST_F(MediaRouterMojoImplTest, CreateRoute) {
195 MediaSource media_source(kSource);
196 MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false, "",
197 false);
198
199 // Use a lambda function as an invocation target here to work around
200 // a limitation with GMock::Invoke that prevents it from using move-only types
201 // in runnable parameter lists.
202 EXPECT_CALL(mock_media_route_provider_,
203 CreateRoute(mojo::String(kSource), mojo::String(kSinkId), _,
204 mojo::String(kOrigin), kInvalidTabId, _, _, _))
205 .WillOnce(Invoke(
206 [](const mojo::String& source, const mojo::String& sink,
207 const mojo::String& presentation_id, const mojo::String& origin,
208 int tab_id, int64_t timeout_millis, bool off_the_record,
209 const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
210 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
211 route->media_source = kSource;
212 route->media_sink_id = kSinkId;
213 route->media_route_id = kRouteId;
214 route->description = kDescription;
215 route->is_local = true;
216 route->for_display = true;
217 route->off_the_record = false;
218 cb.Run(std::move(route), mojo::String(),
219 interfaces::RouteRequestResultCode::OK);
220 }));
221
222 base::RunLoop run_loop;
223 RouteResponseCallbackHandler handler;
224 EXPECT_CALL(handler, DoInvoke(Pointee(Equals(expected_route)), Not(""), "",
225 RouteRequestResult::OK))
226 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
227 std::vector<MediaRouteResponseCallback> route_response_callbacks;
228 route_response_callbacks.push_back(base::Bind(
229 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
230 router()->CreateRoute(
231 kSource, kSinkId, GURL(kOrigin), nullptr, route_response_callbacks,
232 base::TimeDelta::FromMilliseconds(kTimeoutMillis), false);
233 run_loop.Run();
234 }
235
236 TEST_F(MediaRouterMojoImplTest, CreateRouteFails) {
237 EXPECT_CALL(
238 mock_media_route_provider_,
239 CreateRoute(mojo::String(kSource), mojo::String(kSinkId), _,
240 mojo::String(kOrigin), kInvalidTabId, kTimeoutMillis, _, _))
241 .WillOnce(Invoke(
242 [](const mojo::String& source, const mojo::String& sink,
243 const mojo::String& presentation_id, const mojo::String& origin,
244 int tab_id, int64_t timeout_millis, bool off_the_record,
245 const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
246 cb.Run(interfaces::MediaRoutePtr(), mojo::String(kError),
247 interfaces::RouteRequestResultCode::TIMED_OUT);
248 }));
249
250 RouteResponseCallbackHandler handler;
251 base::RunLoop run_loop;
252 EXPECT_CALL(handler,
253 DoInvoke(nullptr, "", kError, RouteRequestResult::TIMED_OUT))
254 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
255 std::vector<MediaRouteResponseCallback> route_response_callbacks;
256 route_response_callbacks.push_back(base::Bind(
257 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
258 router()->CreateRoute(
259 kSource, kSinkId, GURL(kOrigin), nullptr, route_response_callbacks,
260 base::TimeDelta::FromMilliseconds(kTimeoutMillis), false);
261 run_loop.Run();
262 }
263
264 TEST_F(MediaRouterMojoImplTest, CreateRouteOffTheRecordMismatchFails) {
265 EXPECT_CALL(mock_media_route_provider_,
266 CreateRoute(mojo::String(kSource), mojo::String(kSinkId), _,
267 mojo::String(kOrigin), kInvalidTabId, kTimeoutMillis,
268 true, _))
269 .WillOnce(Invoke(
270 [](const mojo::String& source, const mojo::String& sink,
271 const mojo::String& presentation_id, const mojo::String& origin,
272 int tab_id, int64_t timeout_millis, bool off_the_record,
273 const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
274 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
275 route->media_source = kSource;
276 route->media_sink_id = kSinkId;
277 route->media_route_id = kRouteId;
278 route->description = kDescription;
279 route->is_local = true;
280 route->for_display = true;
281 route->off_the_record = false;
282 cb.Run(std::move(route), mojo::String(),
283 interfaces::RouteRequestResultCode::OK);
284 }));
285
286 RouteResponseCallbackHandler handler;
287 base::RunLoop run_loop;
288 std::string error(
289 "Mismatch in off the record status: request = 1, response = 0");
290 EXPECT_CALL(handler, DoInvoke(nullptr, "", error,
291 RouteRequestResult::OFF_THE_RECORD_MISMATCH))
292 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
293 std::vector<MediaRouteResponseCallback> route_response_callbacks;
294 route_response_callbacks.push_back(base::Bind(
295 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
296 router()->CreateRoute(
297 kSource, kSinkId, GURL(kOrigin), nullptr, route_response_callbacks,
298 base::TimeDelta::FromMilliseconds(kTimeoutMillis), true);
299 run_loop.Run();
300 }
301
302 TEST_F(MediaRouterMojoImplTest, OffTheRecordRoutesTerminatedOnProfileShutdown) {
303 EXPECT_CALL(mock_media_route_provider_,
304 CreateRoute(mojo::String(kSource), mojo::String(kSinkId), _,
305 mojo::String(kOrigin), kInvalidTabId, kTimeoutMillis,
306 true, _))
307 .WillOnce(Invoke(
308 [](const mojo::String& source, const mojo::String& sink,
309 const mojo::String& presentation_id, const mojo::String& origin,
310 int tab_id, int64_t timeout_millis, bool off_the_record,
311 const interfaces::MediaRouteProvider::CreateRouteCallback& cb) {
312 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
313 route->media_source = kSource;
314 route->media_sink_id = kSinkId;
315 route->media_route_id = kRouteId;
316 route->description = kDescription;
317 route->is_local = true;
318 route->for_display = true;
319 route->off_the_record = true;
320 cb.Run(std::move(route), mojo::String(),
321 interfaces::RouteRequestResultCode::OK);
322 }));
323 base::RunLoop run_loop;
324 router()->CreateRoute(kSource, kSinkId, GURL(kOrigin), nullptr,
325 std::vector<MediaRouteResponseCallback>(),
326 base::TimeDelta::FromMilliseconds(kTimeoutMillis),
327 true);
328
329 // TODO(mfoltz): Where possible, convert other tests to use RunUntilIdle
330 // instead of manually calling Run/Quit on the run loop.
331 run_loop.RunUntilIdle();
332
333 EXPECT_CALL(mock_media_route_provider_,
334 TerminateRoute(mojo::String(kRouteId)));
335 base::RunLoop run_loop2;
336 router()->OnOffTheRecordProfileShutdown();
337 run_loop2.RunUntilIdle();
338 }
339
340 TEST_F(MediaRouterMojoImplTest, JoinRoute) {
341 MediaSource media_source(kSource);
342 MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false, "",
343 false);
344 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
345 route->media_source = kSource;
346 route->media_sink_id = kSinkId;
347 route->media_route_id = kRouteId;
348 route->description = kDescription;
349 route->is_local = true;
350 route->for_display = true;
351 route->off_the_record = false;
352
353 // Use a lambda function as an invocation target here to work around
354 // a limitation with GMock::Invoke that prevents it from using move-only types
355 // in runnable parameter lists.
356 EXPECT_CALL(
357 mock_media_route_provider_,
358 JoinRoute(mojo::String(kSource), mojo::String(kPresentationId),
359 mojo::String(kOrigin), kInvalidTabId, kTimeoutMillis, _, _))
360 .WillOnce(Invoke([&route](
361 const mojo::String& source, const mojo::String& presentation_id,
362 const mojo::String& origin, int tab_id, int64_t timeout_millis,
363 bool off_the_record,
364 const interfaces::MediaRouteProvider::JoinRouteCallback& cb) {
365 cb.Run(std::move(route), mojo::String(),
366 interfaces::RouteRequestResultCode::OK);
367 }));
368
369 RouteResponseCallbackHandler handler;
370 base::RunLoop run_loop;
371 EXPECT_CALL(handler, DoInvoke(Pointee(Equals(expected_route)), Not(""), "",
372 RouteRequestResult::OK))
373 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
374 std::vector<MediaRouteResponseCallback> route_response_callbacks;
375 route_response_callbacks.push_back(base::Bind(
376 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
377 router()->JoinRoute(kSource, kPresentationId, GURL(kOrigin), nullptr,
378 route_response_callbacks,
379 base::TimeDelta::FromMilliseconds(kTimeoutMillis), false);
380 run_loop.Run();
381 }
382
383 TEST_F(MediaRouterMojoImplTest, JoinRouteFails) {
384 EXPECT_CALL(
385 mock_media_route_provider_,
386 JoinRoute(mojo::String(kSource), mojo::String(kPresentationId),
387 mojo::String(kOrigin), kInvalidTabId, kTimeoutMillis, _, _))
388 .WillOnce(Invoke(
389 [](const mojo::String& source, const mojo::String& presentation_id,
390 const mojo::String& origin, int tab_id, int64_t timeout_millis,
391 bool off_the_record,
392 const interfaces::MediaRouteProvider::JoinRouteCallback& cb) {
393 cb.Run(interfaces::MediaRoutePtr(), mojo::String(kError),
394 interfaces::RouteRequestResultCode::TIMED_OUT);
395 }));
396
397 RouteResponseCallbackHandler handler;
398 base::RunLoop run_loop;
399 EXPECT_CALL(handler,
400 DoInvoke(nullptr, "", kError, RouteRequestResult::TIMED_OUT))
401 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
402 std::vector<MediaRouteResponseCallback> route_response_callbacks;
403 route_response_callbacks.push_back(base::Bind(
404 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
405 router()->JoinRoute(kSource, kPresentationId, GURL(kOrigin), nullptr,
406 route_response_callbacks,
407 base::TimeDelta::FromMilliseconds(kTimeoutMillis), false);
408 run_loop.Run();
409 }
410
411 TEST_F(MediaRouterMojoImplTest, JoinRouteOffTheRecordMismatchFails) {
412 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
413 route->media_source = kSource;
414 route->media_sink_id = kSinkId;
415 route->media_route_id = kRouteId;
416 route->description = kDescription;
417 route->is_local = true;
418 route->for_display = true;
419 route->off_the_record = false;
420
421 // Use a lambda function as an invocation target here to work around
422 // a limitation with GMock::Invoke that prevents it from using move-only types
423 // in runnable parameter lists.
424 EXPECT_CALL(
425 mock_media_route_provider_,
426 JoinRoute(mojo::String(kSource), mojo::String(kPresentationId),
427 mojo::String(kOrigin), kInvalidTabId, kTimeoutMillis, true, _))
428 .WillOnce(Invoke([&route](
429 const mojo::String& source, const mojo::String& presentation_id,
430 const mojo::String& origin, int tab_id, int64_t timeout_millis,
431 bool off_the_record,
432 const interfaces::MediaRouteProvider::JoinRouteCallback& cb) {
433 cb.Run(std::move(route), mojo::String(),
434 interfaces::RouteRequestResultCode::OK);
435 }));
436
437 RouteResponseCallbackHandler handler;
438 base::RunLoop run_loop;
439 std::string error(
440 "Mismatch in off the record status: request = 1, response = 0");
441 EXPECT_CALL(handler, DoInvoke(nullptr, "", error,
442 RouteRequestResult::OFF_THE_RECORD_MISMATCH))
443 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
444 std::vector<MediaRouteResponseCallback> route_response_callbacks;
445 route_response_callbacks.push_back(base::Bind(
446 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
447 router()->JoinRoute(kSource, kPresentationId, GURL(kOrigin), nullptr,
448 route_response_callbacks,
449 base::TimeDelta::FromMilliseconds(kTimeoutMillis), true);
450 run_loop.Run();
451 }
452
453 TEST_F(MediaRouterMojoImplTest, ConnectRouteByRouteId) {
454 MediaSource media_source(kSource);
455 MediaRoute expected_route(kRouteId, media_source, kSinkId, "", false, "",
456 false);
457 expected_route.set_off_the_record(false);
458 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
459 route->media_source = kSource;
460 route->media_sink_id = kSinkId;
461 route->media_route_id = kRouteId;
462 route->description = kDescription;
463 route->is_local = true;
464 route->for_display = true;
465 route->off_the_record = false;
466
467 // Use a lambda function as an invocation target here to work around
468 // a limitation with GMock::Invoke that prevents it from using move-only types
469 // in runnable parameter lists.
470 EXPECT_CALL(
471 mock_media_route_provider_,
472 ConnectRouteByRouteId(mojo::String(kSource), mojo::String(kRouteId), _,
473 mojo::String(kOrigin), kInvalidTabId,
474 kTimeoutMillis, false, _))
475 .WillOnce(Invoke([&route](
476 const mojo::String& source, const mojo::String& route_id,
477 const mojo::String& presentation_id, const mojo::String& origin,
478 int tab_id, int64_t timeout_millis, bool off_the_record,
479 const interfaces::MediaRouteProvider::JoinRouteCallback& cb) {
480 cb.Run(std::move(route), mojo::String(),
481 interfaces::RouteRequestResultCode::OK);
482 }));
483
484 RouteResponseCallbackHandler handler;
485 base::RunLoop run_loop;
486 EXPECT_CALL(handler, DoInvoke(Pointee(Equals(expected_route)), Not(""), "",
487 RouteRequestResult::OK))
488 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
489 std::vector<MediaRouteResponseCallback> route_response_callbacks;
490 route_response_callbacks.push_back(base::Bind(
491 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
492 router()->ConnectRouteByRouteId(
493 kSource, kRouteId, GURL(kOrigin), nullptr, route_response_callbacks,
494 base::TimeDelta::FromMilliseconds(kTimeoutMillis), false);
495 run_loop.Run();
496 }
497
498 TEST_F(MediaRouterMojoImplTest, ConnectRouteByRouteIdFails) {
499 EXPECT_CALL(
500 mock_media_route_provider_,
501 ConnectRouteByRouteId(mojo::String(kSource), mojo::String(kRouteId), _,
502 mojo::String(kOrigin), kInvalidTabId,
503 kTimeoutMillis, true, _))
504 .WillOnce(Invoke(
505 [](const mojo::String& source, const mojo::String& route_id,
506 const mojo::String& presentation_id, const mojo::String& origin,
507 int tab_id, int64_t timeout_millis, bool off_the_record,
508 const interfaces::MediaRouteProvider::JoinRouteCallback& cb) {
509 cb.Run(interfaces::MediaRoutePtr(), mojo::String(kError),
510 interfaces::RouteRequestResultCode::TIMED_OUT);
511 }));
512
513 RouteResponseCallbackHandler handler;
514 base::RunLoop run_loop;
515 EXPECT_CALL(handler,
516 DoInvoke(nullptr, "", kError, RouteRequestResult::TIMED_OUT))
517 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
518 std::vector<MediaRouteResponseCallback> route_response_callbacks;
519 route_response_callbacks.push_back(base::Bind(
520 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
521 router()->ConnectRouteByRouteId(
522 kSource, kRouteId, GURL(kOrigin), nullptr, route_response_callbacks,
523 base::TimeDelta::FromMilliseconds(kTimeoutMillis), true);
524 run_loop.Run();
525 }
526
527 TEST_F(MediaRouterMojoImplTest, ConnectRouteByIdOffTheRecordMismatchFails) {
528 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New();
529 route->media_source = kSource;
530 route->media_sink_id = kSinkId;
531 route->media_route_id = kRouteId;
532 route->description = kDescription;
533 route->is_local = true;
534 route->for_display = true;
535 route->off_the_record = false;
536
537 // Use a lambda function as an invocation target here to work around
538 // a limitation with GMock::Invoke that prevents it from using move-only types
539 // in runnable parameter lists.
540 EXPECT_CALL(
541 mock_media_route_provider_,
542 ConnectRouteByRouteId(mojo::String(kSource), mojo::String(kRouteId), _,
543 mojo::String(kOrigin), kInvalidTabId,
544 kTimeoutMillis, true, _))
545 .WillOnce(Invoke([&route](
546 const mojo::String& source, const mojo::String& route_id,
547 const mojo::String& presentation_id, const mojo::String& origin,
548 int tab_id, int64_t timeout_millis, bool off_the_record,
549 const interfaces::MediaRouteProvider::JoinRouteCallback& cb) {
550 cb.Run(std::move(route), mojo::String(),
551 interfaces::RouteRequestResultCode::OK);
552 }));
553
554 RouteResponseCallbackHandler handler;
555 base::RunLoop run_loop;
556 std::string error(
557 "Mismatch in off the record status: request = 1, response = 0");
558 EXPECT_CALL(handler, DoInvoke(nullptr, "", error,
559 RouteRequestResult::OFF_THE_RECORD_MISMATCH))
560 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
561 std::vector<MediaRouteResponseCallback> route_response_callbacks;
562 route_response_callbacks.push_back(base::Bind(
563 &RouteResponseCallbackHandler::Invoke, base::Unretained(&handler)));
564 router()->ConnectRouteByRouteId(
565 kSource, kRouteId, GURL(kOrigin), nullptr, route_response_callbacks,
566 base::TimeDelta::FromMilliseconds(kTimeoutMillis), true);
567 run_loop.Run();
568 }
569
570 TEST_F(MediaRouterMojoImplTest, DetachRoute) {
571 base::RunLoop run_loop;
572 EXPECT_CALL(mock_media_route_provider_, DetachRoute(mojo::String(kRouteId)))
573 .WillOnce(InvokeWithoutArgs([&run_loop]() {
574 run_loop.Quit();
575 }));
576 router()->DetachRoute(kRouteId);
577 run_loop.Run();
578 }
579
580 TEST_F(MediaRouterMojoImplTest, TerminateRoute) {
581 base::RunLoop run_loop;
582 EXPECT_CALL(mock_media_route_provider_,
583 TerminateRoute(mojo::String(kRouteId)))
584 .WillOnce(InvokeWithoutArgs([&run_loop]() {
585 run_loop.Quit();
586 }));
587 router()->TerminateRoute(kRouteId);
588 run_loop.Run();
589 }
590
591 TEST_F(MediaRouterMojoImplTest, HandleIssue) {
592 MockIssuesObserver issue_observer1(router());
593 MockIssuesObserver issue_observer2(router());
594 issue_observer1.RegisterObserver();
595 issue_observer2.RegisterObserver();
596
597 interfaces::IssuePtr mojo_issue1 = CreateMojoIssue("title 1");
598 const Issue& expected_issue1 = mojo_issue1.To<Issue>();
599
600 const Issue* issue;
601 EXPECT_CALL(issue_observer1,
602 OnIssueUpdated(Pointee(EqualsIssue(expected_issue1))))
603 .WillOnce(SaveArg<0>(&issue));
604 base::RunLoop run_loop;
605 EXPECT_CALL(issue_observer2,
606 OnIssueUpdated(Pointee(EqualsIssue(expected_issue1))))
607 .WillOnce(InvokeWithoutArgs([&run_loop]() {
608 run_loop.Quit();
609 }));
610 media_router_proxy_->OnIssue(std::move(mojo_issue1));
611 run_loop.Run();
612
613 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&issue_observer1));
614 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&issue_observer2));
615
616 EXPECT_CALL(issue_observer1, OnIssueUpdated(nullptr));
617 EXPECT_CALL(issue_observer2, OnIssueUpdated(nullptr));
618
619 router()->ClearIssue(issue->id());
620
621 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&issue_observer1));
622 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&issue_observer2));
623 router()->UnregisterIssuesObserver(&issue_observer1);
624 interfaces::IssuePtr mojo_issue2 = CreateMojoIssue("title 2");
625 const Issue& expected_issue2 = mojo_issue2.To<Issue>();
626
627 EXPECT_CALL(issue_observer2,
628 OnIssueUpdated(Pointee(EqualsIssue(expected_issue2))));
629 router()->AddIssue(expected_issue2);
630 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&issue_observer2));
631
632 EXPECT_CALL(issue_observer2, OnIssueUpdated(nullptr));
633 router()->ClearIssue(issue->id());
634 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&issue_observer2));
635
636 base::RunLoop run_loop2;
637 EXPECT_CALL(issue_observer2,
638 OnIssueUpdated(Pointee(EqualsIssue(expected_issue2))))
639 .WillOnce(InvokeWithoutArgs([&run_loop2]() {
640 run_loop2.Quit();
641 }));
642 media_router_proxy_->OnIssue(std::move(mojo_issue2));
643 run_loop2.Run();
644
645 issue_observer1.UnregisterObserver();
646 issue_observer2.UnregisterObserver();
647 }
648
649 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
650 router()->OnSinkAvailabilityUpdated(
651 interfaces::MediaRouter::SinkAvailability::AVAILABLE);
652 MediaSource media_source(kSource);
653 GURL origin("https://google.com");
654
655 // These should only be called once even if there is more than one observer
656 // for a given source.
657 EXPECT_CALL(mock_media_route_provider_,
658 StartObservingMediaSinks(mojo::String(kSource)));
659 EXPECT_CALL(mock_media_route_provider_,
660 StartObservingMediaSinks(mojo::String(kSource2)));
661
662 scoped_ptr<MockMediaSinksObserver> sinks_observer(
663 new MockMediaSinksObserver(router(), media_source, origin));
664 EXPECT_TRUE(sinks_observer->Init());
665 scoped_ptr<MockMediaSinksObserver> extra_sinks_observer(
666 new MockMediaSinksObserver(router(), media_source, origin));
667 EXPECT_TRUE(extra_sinks_observer->Init());
668 scoped_ptr<MockMediaSinksObserver> unrelated_sinks_observer(
669 new MockMediaSinksObserver(router(), MediaSource(kSource2), origin));
670 EXPECT_TRUE(unrelated_sinks_observer->Init());
671 ProcessEventLoop();
672
673 std::vector<MediaSink> expected_sinks;
674 expected_sinks.push_back(
675 MediaSink(kSinkId, kSinkName, MediaSink::IconType::CAST));
676 expected_sinks.push_back(
677 MediaSink(kSinkId2, kSinkName, MediaSink::IconType::CAST));
678
679 mojo::Array<interfaces::MediaSinkPtr> mojo_sinks(2);
680 mojo_sinks[0] = interfaces::MediaSink::New();
681 mojo_sinks[0]->sink_id = kSinkId;
682 mojo_sinks[0]->name = kSinkName;
683 mojo_sinks[0]->icon_type =
684 media_router::interfaces::MediaSink::IconType::CAST;
685 mojo_sinks[1] = interfaces::MediaSink::New();
686 mojo_sinks[1]->sink_id = kSinkId2;
687 mojo_sinks[1]->name = kSinkName;
688 mojo_sinks[1]->icon_type =
689 media_router::interfaces::MediaSink::IconType::CAST;
690
691 mojo::Array<mojo::String> mojo_origins(1);
692 mojo_origins[0] = origin.spec();
693
694 base::RunLoop run_loop;
695 EXPECT_CALL(*sinks_observer, OnSinksReceived(SequenceEquals(expected_sinks)));
696 EXPECT_CALL(*extra_sinks_observer,
697 OnSinksReceived(SequenceEquals(expected_sinks)))
698 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
699 media_router_proxy_->OnSinksReceived(media_source.id(), std::move(mojo_sinks),
700 std::move(mojo_origins));
701 run_loop.Run();
702
703 // Since the MediaRouterMojoImpl has already received results for
704 // |media_source|, return cached results to observers that are subsequently
705 // registered.
706 scoped_ptr<MockMediaSinksObserver> cached_sinks_observer(
707 new MockMediaSinksObserver(router(), media_source, origin));
708 EXPECT_CALL(*cached_sinks_observer,
709 OnSinksReceived(SequenceEquals(expected_sinks)));
710 EXPECT_TRUE(cached_sinks_observer->Init());
711
712 // Different origin from cached result. Empty list will be returned.
713 scoped_ptr<MockMediaSinksObserver> cached_sinks_observer2(
714 new MockMediaSinksObserver(router(), media_source,
715 GURL("https://youtube.com")));
716 EXPECT_CALL(*cached_sinks_observer2, OnSinksReceived(IsEmpty()));
717 EXPECT_TRUE(cached_sinks_observer2->Init());
718
719 base::RunLoop run_loop2;
720 EXPECT_CALL(mock_media_route_provider_,
721 StopObservingMediaSinks(mojo::String(kSource)));
722 EXPECT_CALL(mock_media_route_provider_,
723 StopObservingMediaSinks(mojo::String(kSource2)))
724 .WillOnce(InvokeWithoutArgs([&run_loop2]() {
725 run_loop2.Quit();
726 }));
727 sinks_observer.reset();
728 extra_sinks_observer.reset();
729 unrelated_sinks_observer.reset();
730 cached_sinks_observer.reset();
731 cached_sinks_observer2.reset();
732 run_loop2.Run();
733 }
734
735 TEST_F(MediaRouterMojoImplTest,
736 RegisterMediaSinksObserverWithAvailabilityChange) {
737 GURL origin("https://google.com");
738
739 // When availability is UNAVAILABLE, no calls should be made to MRPM.
740 router()->OnSinkAvailabilityUpdated(
741 interfaces::MediaRouter::SinkAvailability::UNAVAILABLE);
742 MediaSource media_source(kSource);
743 scoped_ptr<MockMediaSinksObserver> sinks_observer(
744 new MockMediaSinksObserver(router(), media_source, origin));
745 EXPECT_CALL(*sinks_observer, OnSinksReceived(IsEmpty()));
746 EXPECT_TRUE(sinks_observer->Init());
747 MediaSource media_source2(kSource2);
748 scoped_ptr<MockMediaSinksObserver> sinks_observer2(
749 new MockMediaSinksObserver(router(), media_source2, origin));
750 EXPECT_CALL(*sinks_observer2, OnSinksReceived(IsEmpty()));
751 EXPECT_TRUE(sinks_observer2->Init());
752 EXPECT_CALL(mock_media_route_provider_,
753 StartObservingMediaSinks(mojo::String(kSource)))
754 .Times(0);
755 EXPECT_CALL(mock_media_route_provider_,
756 StartObservingMediaSinks(mojo::String(kSource2)))
757 .Times(0);
758 ProcessEventLoop();
759 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_media_route_provider_));
760
761 // When availability transitions AVAILABLE, existing sink queries should be
762 // sent to MRPM.
763 router()->OnSinkAvailabilityUpdated(
764 interfaces::MediaRouter::SinkAvailability::AVAILABLE);
765 EXPECT_CALL(mock_media_route_provider_,
766 StartObservingMediaSinks(mojo::String(kSource)))
767 .Times(1);
768 EXPECT_CALL(mock_media_route_provider_,
769 StartObservingMediaSinks(mojo::String(kSource2)))
770 .Times(1);
771 ProcessEventLoop();
772 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_media_route_provider_));
773
774 // No change in availability status; no calls should be made to MRPM.
775 router()->OnSinkAvailabilityUpdated(
776 interfaces::MediaRouter::SinkAvailability::AVAILABLE);
777 EXPECT_CALL(mock_media_route_provider_,
778 StartObservingMediaSinks(mojo::String(kSource)))
779 .Times(0);
780 EXPECT_CALL(mock_media_route_provider_,
781 StartObservingMediaSinks(mojo::String(kSource2)))
782 .Times(0);
783 ProcessEventLoop();
784 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_media_route_provider_));
785
786 // When availability is UNAVAILABLE, queries are already removed from MRPM.
787 // Unregistering observer won't result in call to MRPM to remove query.
788 router()->OnSinkAvailabilityUpdated(
789 interfaces::MediaRouter::SinkAvailability::UNAVAILABLE);
790 EXPECT_CALL(mock_media_route_provider_,
791 StopObservingMediaSinks(mojo::String(kSource)))
792 .Times(0);
793 sinks_observer.reset();
794 ProcessEventLoop();
795 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_media_route_provider_));
796
797 // When availability is AVAILABLE, call is made to MRPM to remove query when
798 // observer is unregistered.
799 router()->OnSinkAvailabilityUpdated(
800 interfaces::MediaRouter::SinkAvailability::AVAILABLE);
801 EXPECT_CALL(mock_media_route_provider_,
802 StopObservingMediaSinks(mojo::String(kSource2)));
803 sinks_observer2.reset();
804 ProcessEventLoop();
805 }
806
807 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) {
808 MockMediaRouter mock_router;
809 MediaSource media_source(kSource);
810 MediaSource different_media_source(kSource2);
811 EXPECT_CALL(mock_media_route_provider_,
812 StartObservingMediaRoutes(mojo::String(media_source.id()))).Times(2);
813 EXPECT_CALL(mock_media_route_provider_,
814 StartObservingMediaRoutes(
815 mojo::String(different_media_source.id()))).Times(1);
816
817 MediaRoutesObserver* observer_captured;
818 EXPECT_CALL(mock_router, RegisterMediaRoutesObserver(_))
819 .Times(3)
820 .WillRepeatedly(SaveArg<0>(&observer_captured));
821 MockMediaRoutesObserver routes_observer(&mock_router, media_source.id());
822 EXPECT_EQ(observer_captured, &routes_observer);
823 MockMediaRoutesObserver extra_routes_observer(&mock_router,
824 media_source.id());
825 EXPECT_EQ(observer_captured, &extra_routes_observer);
826 MockMediaRoutesObserver different_routes_observer(&mock_router,
827 different_media_source.id());
828 EXPECT_EQ(observer_captured, &different_routes_observer);
829 router()->RegisterMediaRoutesObserver(&routes_observer);
830 router()->RegisterMediaRoutesObserver(&extra_routes_observer);
831 router()->RegisterMediaRoutesObserver(&different_routes_observer);
832
833 std::vector<MediaRoute> expected_routes;
834 expected_routes.push_back(MediaRoute(kRouteId, media_source, kSinkId,
835 kDescription, false, "", false));
836 MediaRoute incognito_expected_route(kRouteId2, media_source, kSinkId,
837 kDescription, false, "", false);
838 incognito_expected_route.set_off_the_record(true);
839 expected_routes.push_back(incognito_expected_route);
840 std::vector<MediaRoute::Id> expected_joinable_route_ids;
841 expected_joinable_route_ids.push_back(kJoinableRouteId);
842 expected_joinable_route_ids.push_back(kJoinableRouteId2);
843
844 mojo::Array<mojo::String> mojo_joinable_routes(2);
845 mojo_joinable_routes[0] = kJoinableRouteId;
846 mojo_joinable_routes[1] = kJoinableRouteId2;
847
848 mojo::Array<interfaces::MediaRoutePtr> mojo_routes(2);
849 mojo_routes[0] = interfaces::MediaRoute::New();
850 mojo_routes[0]->media_route_id = kRouteId;
851 mojo_routes[0]->media_source = kSource;
852 mojo_routes[0]->media_sink_id = kSinkId;
853 mojo_routes[0]->description = kDescription;
854 mojo_routes[0]->is_local = false;
855 mojo_routes[0]->off_the_record = false;
856 mojo_routes[1] = interfaces::MediaRoute::New();
857 mojo_routes[1]->media_route_id = kRouteId2;
858 mojo_routes[1]->media_source = kSource;
859 mojo_routes[1]->media_sink_id = kSinkId;
860 mojo_routes[1]->description = kDescription;
861 mojo_routes[1]->is_local = false;
862 mojo_routes[1]->off_the_record = true;
863
864 EXPECT_CALL(routes_observer,
865 OnRoutesUpdated(SequenceEquals(expected_routes),
866 expected_joinable_route_ids));
867 EXPECT_CALL(extra_routes_observer,
868 OnRoutesUpdated(SequenceEquals(expected_routes),
869 expected_joinable_route_ids));
870 EXPECT_CALL(different_routes_observer,
871 OnRoutesUpdated(SequenceEquals(expected_routes),
872 expected_joinable_route_ids)).Times(0);
873 media_router_proxy_->OnRoutesUpdated(std::move(mojo_routes),
874 media_source.id(),
875 std::move(mojo_joinable_routes));
876 ProcessEventLoop();
877
878 EXPECT_CALL(mock_router, UnregisterMediaRoutesObserver(&routes_observer));
879 EXPECT_CALL(mock_router,
880 UnregisterMediaRoutesObserver(&extra_routes_observer));
881 EXPECT_CALL(mock_router,
882 UnregisterMediaRoutesObserver(&different_routes_observer));
883 router()->UnregisterMediaRoutesObserver(&routes_observer);
884 router()->UnregisterMediaRoutesObserver(&extra_routes_observer);
885 router()->UnregisterMediaRoutesObserver(&different_routes_observer);
886 EXPECT_CALL(mock_media_route_provider_,
887 StopObservingMediaRoutes(
888 mojo::String(media_source.id()))).Times(1);
889 EXPECT_CALL(mock_media_route_provider_,
890 StopObservingMediaRoutes(
891 mojo::String(different_media_source.id())));
892 ProcessEventLoop();
893 }
894
895 TEST_F(MediaRouterMojoImplTest, SendRouteMessage) {
896 EXPECT_CALL(
897 mock_media_route_provider_,
898 SendRouteMessage(mojo::String(kRouteId), mojo::String(kMessage), _))
899 .WillOnce(Invoke([](
900 const MediaRoute::Id& route_id, const std::string& message,
901 const interfaces::MediaRouteProvider::SendRouteMessageCallback& cb) {
902 cb.Run(true);
903 }));
904
905 base::RunLoop run_loop;
906 SendMessageCallbackHandler handler;
907 EXPECT_CALL(handler, Invoke(true))
908 .WillOnce(InvokeWithoutArgs([&run_loop]() {
909 run_loop.Quit();
910 }));
911 router()->SendRouteMessage(kRouteId, kMessage,
912 base::Bind(&SendMessageCallbackHandler::Invoke,
913 base::Unretained(&handler)));
914 run_loop.Run();
915 }
916
917 TEST_F(MediaRouterMojoImplTest, SendRouteBinaryMessage) {
918 scoped_ptr<std::vector<uint8_t>> expected_binary_data(
919 new std::vector<uint8_t>(kBinaryMessage,
920 kBinaryMessage + arraysize(kBinaryMessage)));
921
922 EXPECT_CALL(mock_media_route_provider_,
923 SendRouteBinaryMessageInternal(mojo::String(kRouteId), _, _))
924 .WillOnce(Invoke([](
925 const MediaRoute::Id& route_id, const std::vector<uint8_t>& data,
926 const interfaces::MediaRouteProvider::SendRouteMessageCallback& cb) {
927 EXPECT_EQ(
928 0, memcmp(kBinaryMessage, &(data[0]), arraysize(kBinaryMessage)));
929 cb.Run(true);
930 }));
931
932 base::RunLoop run_loop;
933 SendMessageCallbackHandler handler;
934 EXPECT_CALL(handler, Invoke(true))
935 .WillOnce(InvokeWithoutArgs([&run_loop]() {
936 run_loop.Quit();
937 }));
938 router()->SendRouteBinaryMessage(
939 kRouteId, std::move(expected_binary_data),
940 base::Bind(&SendMessageCallbackHandler::Invoke,
941 base::Unretained(&handler)));
942 run_loop.Run();
943 }
944
945 TEST_F(MediaRouterMojoImplTest, PresentationSessionMessagesSingleObserver) {
946 mojo::Array<interfaces::RouteMessagePtr> mojo_messages(2);
947 mojo_messages[0] = interfaces::RouteMessage::New();
948 mojo_messages[0]->type = interfaces::RouteMessage::Type::TEXT;
949 mojo_messages[0]->message = "text";
950 mojo_messages[1] = interfaces::RouteMessage::New();
951 mojo_messages[1]->type = interfaces::RouteMessage::Type::BINARY;
952 mojo_messages[1]->data.push_back(1);
953
954 ScopedVector<content::PresentationSessionMessage> expected_messages;
955 scoped_ptr<content::PresentationSessionMessage> message;
956 message.reset(new content::PresentationSessionMessage(
957 content::PresentationMessageType::TEXT));
958 message->message = "text";
959 expected_messages.push_back(std::move(message));
960
961 message.reset(new content::PresentationSessionMessage(
962 content::PresentationMessageType::ARRAY_BUFFER));
963 message->data.reset(new std::vector<uint8_t>(1, 1));
964 expected_messages.push_back(std::move(message));
965
966 base::RunLoop run_loop;
967 MediaRoute::Id expected_route_id("foo");
968 interfaces::MediaRouteProvider::ListenForRouteMessagesCallback mojo_callback;
969 EXPECT_CALL(mock_media_route_provider_,
970 ListenForRouteMessages(Eq(expected_route_id), _))
971 .WillOnce(DoAll(SaveArg<1>(&mojo_callback),
972 InvokeWithoutArgs([&run_loop]() {
973 run_loop.Quit();
974 })));
975
976 // |pass_ownership| param is "true" here because there is only one observer.
977 ListenForMessagesCallbackHandler handler(std::move(expected_messages), true);
978
979 EXPECT_CALL(handler, InvokeObserver());
980 // Creating PresentationSessionMessagesObserver will register itself to the
981 // MediaRouter, which in turn will start listening for route messages.
982 scoped_ptr<PresentationSessionMessagesObserver> observer(
983 new PresentationSessionMessagesObserver(
984 base::Bind(&ListenForMessagesCallbackHandler::Invoke,
985 base::Unretained(&handler)),
986 expected_route_id, router()));
987 run_loop.Run();
988
989 base::RunLoop run_loop2;
990 // Simulate messages by invoking the saved mojo callback.
991 // We expect one more ListenForRouteMessages call since |observer| was
992 // still registered when the first set of messages arrived.
993 mojo_callback.Run(std::move(mojo_messages), false);
994 interfaces::MediaRouteProvider::ListenForRouteMessagesCallback
995 mojo_callback_2;
996 EXPECT_CALL(mock_media_route_provider_, ListenForRouteMessages(_, _))
997 .WillOnce(DoAll(SaveArg<1>(&mojo_callback_2),
998 InvokeWithoutArgs([&run_loop2]() {
999 run_loop2.Quit();
1000 })));
1001 run_loop2.Run();
1002
1003 base::RunLoop run_loop3;
1004 // Stop listening for messages. In particular, MediaRouterMojoImpl will not
1005 // call ListenForRouteMessages again when it sees there are no more observers.
1006 mojo::Array<interfaces::RouteMessagePtr> mojo_messages_2(1);
1007 mojo_messages_2[0] = interfaces::RouteMessage::New();
1008 mojo_messages_2[0]->type = interfaces::RouteMessage::Type::TEXT;
1009 mojo_messages_2[0]->message = "foo";
1010 observer.reset();
1011 mojo_callback_2.Run(std::move(mojo_messages_2), false);
1012 EXPECT_CALL(mock_media_route_provider_, StopListeningForRouteMessages(_))
1013 .WillOnce(InvokeWithoutArgs([&run_loop3]() {
1014 run_loop3.Quit();
1015 }));
1016 run_loop3.Run();
1017 }
1018
1019 TEST_F(MediaRouterMojoImplTest, PresentationSessionMessagesMultipleObservers) {
1020 mojo::Array<interfaces::RouteMessagePtr> mojo_messages(2);
1021 mojo_messages[0] = interfaces::RouteMessage::New();
1022 mojo_messages[0]->type = interfaces::RouteMessage::Type::TEXT;
1023 mojo_messages[0]->message = "text";
1024 mojo_messages[1] = interfaces::RouteMessage::New();
1025 mojo_messages[1]->type = interfaces::RouteMessage::Type::BINARY;
1026 mojo_messages[1]->data.push_back(1);
1027
1028 ScopedVector<content::PresentationSessionMessage> expected_messages;
1029 scoped_ptr<content::PresentationSessionMessage> message;
1030 message.reset(new content::PresentationSessionMessage(
1031 content::PresentationMessageType::TEXT));
1032 message->message = "text";
1033 expected_messages.push_back(std::move(message));
1034
1035 message.reset(new content::PresentationSessionMessage(
1036 content::PresentationMessageType::ARRAY_BUFFER));
1037 message->data.reset(new std::vector<uint8_t>(1, 1));
1038 expected_messages.push_back(std::move(message));
1039
1040 base::RunLoop run_loop;
1041 MediaRoute::Id expected_route_id("foo");
1042 interfaces::MediaRouteProvider::ListenForRouteMessagesCallback mojo_callback;
1043 EXPECT_CALL(mock_media_route_provider_,
1044 ListenForRouteMessages(Eq(expected_route_id), _))
1045 .WillOnce(DoAll(SaveArg<1>(&mojo_callback),
1046 InvokeWithoutArgs([&run_loop]() {
1047 run_loop.Quit();
1048 })));
1049
1050 // |pass_ownership| param is "false" here because there are more than one
1051 // observers.
1052 ListenForMessagesCallbackHandler handler(std::move(expected_messages), false);
1053
1054 EXPECT_CALL(handler, InvokeObserver()).Times(2);
1055 // Creating PresentationSessionMessagesObserver will register itself to the
1056 // MediaRouter, which in turn will start listening for route messages.
1057 scoped_ptr<PresentationSessionMessagesObserver> observer1(
1058 new PresentationSessionMessagesObserver(
1059 base::Bind(&ListenForMessagesCallbackHandler::Invoke,
1060 base::Unretained(&handler)),
1061 expected_route_id, router()));
1062 scoped_ptr<PresentationSessionMessagesObserver> observer2(
1063 new PresentationSessionMessagesObserver(
1064 base::Bind(&ListenForMessagesCallbackHandler::Invoke,
1065 base::Unretained(&handler)),
1066 expected_route_id, router()));
1067 run_loop.Run();
1068
1069 base::RunLoop run_loop2;
1070 // Simulate messages by invoking the saved mojo callback.
1071 // We expect one more ListenForRouteMessages call since |observer| was
1072 // still registered when the first set of messages arrived.
1073 mojo_callback.Run(std::move(mojo_messages), false);
1074 interfaces::MediaRouteProvider::ListenForRouteMessagesCallback
1075 mojo_callback_2;
1076 EXPECT_CALL(mock_media_route_provider_, ListenForRouteMessages(_, _))
1077 .WillOnce(DoAll(SaveArg<1>(&mojo_callback_2),
1078 InvokeWithoutArgs([&run_loop2]() {
1079 run_loop2.Quit();
1080 })));
1081 run_loop2.Run();
1082
1083 base::RunLoop run_loop3;
1084 // Stop listening for messages. In particular, MediaRouterMojoImpl will not
1085 // call ListenForRouteMessages again when it sees there are no more observers.
1086 mojo::Array<interfaces::RouteMessagePtr> mojo_messages_2(1);
1087 mojo_messages_2[0] = interfaces::RouteMessage::New();
1088 mojo_messages_2[0]->type = interfaces::RouteMessage::Type::TEXT;
1089 mojo_messages_2[0]->message = "foo";
1090 observer1.reset();
1091 observer2.reset();
1092 mojo_callback_2.Run(std::move(mojo_messages_2), false);
1093 EXPECT_CALL(mock_media_route_provider_, StopListeningForRouteMessages(_))
1094 .WillOnce(InvokeWithoutArgs([&run_loop3]() {
1095 run_loop3.Quit();
1096 }));
1097 run_loop3.Run();
1098 }
1099
1100 TEST_F(MediaRouterMojoImplTest, PresentationSessionMessagesError) {
1101 MediaRoute::Id expected_route_id("foo");
1102 interfaces::MediaRouteProvider::ListenForRouteMessagesCallback mojo_callback;
1103 base::RunLoop run_loop;
1104 EXPECT_CALL(mock_media_route_provider_,
1105 ListenForRouteMessages(Eq(expected_route_id), _))
1106 .WillOnce(DoAll(SaveArg<1>(&mojo_callback),
1107 InvokeWithoutArgs([&run_loop]() {
1108 run_loop.Quit();
1109 })));
1110
1111 ListenForMessagesCallbackHandler handler(
1112 ScopedVector<content::PresentationSessionMessage>(), true);
1113
1114 // Creating PresentationSessionMessagesObserver will register itself to the
1115 // MediaRouter, which in turn will start listening for route messages.
1116 scoped_ptr<PresentationSessionMessagesObserver> observer1(
1117 new PresentationSessionMessagesObserver(
1118 base::Bind(&ListenForMessagesCallbackHandler::Invoke,
1119 base::Unretained(&handler)),
1120 expected_route_id, router()));
1121 run_loop.Run();
1122
1123 mojo_callback.Run(mojo::Array<interfaces::RouteMessagePtr>(), true);
1124 ProcessEventLoop();
1125 }
1126
1127 TEST_F(MediaRouterMojoImplTest, PresentationConnectionStateChangedCallback) {
1128 MediaRoute::Id route_id("route-id");
1129 const std::string kPresentationUrl("http://foo.fakeUrl");
1130 const std::string kPresentationId("pid");
1131 content::PresentationSessionInfo connection(kPresentationUrl,
1132 kPresentationId);
1133 MockPresentationConnectionStateChangedCallback callback;
1134 scoped_ptr<PresentationConnectionStateSubscription> subscription =
1135 router()->AddPresentationConnectionStateChangedCallback(
1136 route_id,
1137 base::Bind(&MockPresentationConnectionStateChangedCallback::Run,
1138 base::Unretained(&callback)));
1139
1140 {
1141 base::RunLoop run_loop;
1142 content::PresentationConnectionStateChangeInfo closed_info(
1143 content::PRESENTATION_CONNECTION_STATE_CLOSED);
1144 closed_info.close_reason =
1145 content::PRESENTATION_CONNECTION_CLOSE_REASON_WENT_AWAY;
1146 closed_info.message = "Foo";
1147
1148 EXPECT_CALL(callback, Run(StateChageInfoEquals(closed_info)))
1149 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
1150 media_router_proxy_->OnPresentationConnectionClosed(
1151 route_id, PresentationConnectionCloseReason::WENT_AWAY, "Foo");
1152 run_loop.Run();
1153 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&callback));
1154 }
1155
1156 content::PresentationConnectionStateChangeInfo terminated_info(
1157 content::PRESENTATION_CONNECTION_STATE_TERMINATED);
1158 {
1159 base::RunLoop run_loop;
1160 EXPECT_CALL(callback, Run(StateChageInfoEquals(terminated_info)))
1161 .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
1162 media_router_proxy_->OnPresentationConnectionStateChanged(
1163 route_id, PresentationConnectionState::TERMINATED);
1164 run_loop.Run();
1165
1166 EXPECT_TRUE(Mock::VerifyAndClearExpectations(&callback));
1167 }
1168 }
1169
1170 TEST_F(MediaRouterMojoImplTest,
1171 PresentationConnectionStateChangedCallbackRemoved) {
1172 MediaRoute::Id route_id("route-id");
1173 MockPresentationConnectionStateChangedCallback callback;
1174 scoped_ptr<PresentationConnectionStateSubscription> subscription =
1175 router()->AddPresentationConnectionStateChangedCallback(
1176 route_id,
1177 base::Bind(&MockPresentationConnectionStateChangedCallback::Run,
1178 base::Unretained(&callback)));
1179
1180 // Callback has been removed, so we don't expect it to be called anymore.
1181 subscription.reset();
1182 EXPECT_TRUE(router()->presentation_connection_state_callbacks_.empty());
1183
1184 EXPECT_CALL(callback, Run(_)).Times(0);
1185 media_router_proxy_->OnPresentationConnectionStateChanged(
1186 route_id, PresentationConnectionState::TERMINATED);
1187 ProcessEventLoop();
1188 }
1189
1190 TEST_F(MediaRouterMojoImplTest, QueuedWhileAsleep) {
1191 base::RunLoop run_loop;
1192 EXPECT_CALL(mock_event_page_tracker_, IsEventPageSuspended(extension_id()))
1193 .Times(2)
1194 .WillRepeatedly(Return(true));
1195 EXPECT_CALL(mock_event_page_tracker_, WakeEventPage(extension_id(), _))
1196 .Times(2)
1197 .WillOnce(Return(true))
1198 .WillOnce(DoAll(InvokeWithoutArgs([&run_loop]() {
1199 run_loop.Quit();
1200 }), Return(true)));
1201 router()->DetachRoute(kRouteId);
1202 router()->DetachRoute(kRouteId2);
1203 run_loop.Run();
1204 EXPECT_CALL(mock_event_page_tracker_, IsEventPageSuspended(extension_id()))
1205 .Times(1)
1206 .WillRepeatedly(Return(false));
1207 EXPECT_CALL(mock_media_route_provider_, DetachRoute(mojo::String(kRouteId)));
1208 EXPECT_CALL(mock_media_route_provider_, DetachRoute(mojo::String(kRouteId2)));
1209 ConnectProviderManagerService();
1210 ProcessEventLoop();
1211 }
1212
1213 class MediaRouterMojoExtensionTest : public ::testing::Test {
1214 public:
1215 MediaRouterMojoExtensionTest()
1216 : process_manager_(nullptr),
1217 message_loop_(mojo::common::MessagePumpMojo::Create()) {}
1218
1219 ~MediaRouterMojoExtensionTest() override {}
1220
1221 protected:
1222 void SetUp() override {
1223 // Set the extension's version number to be identical to the browser's.
1224 extension_ =
1225 extensions::test_util::BuildExtension(extensions::ExtensionBuilder())
1226 .MergeManifest(extensions::DictionaryBuilder()
1227 .Set("version", version_info::GetVersionNumber())
1228 .Build())
1229 .Build();
1230
1231 profile_.reset(new TestingProfile);
1232 // Set up a mock ProcessManager instance.
1233 extensions::ProcessManagerFactory::GetInstance()->SetTestingFactory(
1234 profile_.get(), &TestProcessManager::Create);
1235 process_manager_ = static_cast<TestProcessManager*>(
1236 extensions::ProcessManager::Get(profile_.get()));
1237 DCHECK(process_manager_);
1238
1239 // Create MR and its proxy, so that it can be accessed through Mojo.
1240 media_router_.reset(new MediaRouterMojoImpl(process_manager_));
1241 ProcessEventLoop();
1242 }
1243
1244 void TearDown() override {
1245 media_router_.reset();
1246 profile_.reset();
1247 // Explicitly delete the TestingBrowserProcess before |message_loop_|.
1248 // This allows it to do cleanup before |message_loop_| goes away.
1249 TestingBrowserProcess::DeleteInstance();
1250 }
1251
1252 // Constructs bindings so that |media_router_| delegates calls to
1253 // |mojo_media_router_|, which are then handled by
1254 // |mock_media_route_provider_service_|.
1255 void BindMediaRouteProvider() {
1256 binding_.reset(new mojo::Binding<interfaces::MediaRouteProvider>(
1257 &mock_media_route_provider_,
1258 mojo::GetProxy(&media_route_provider_proxy_)));
1259 media_router_->BindToMojoRequest(mojo::GetProxy(&media_router_proxy_),
1260 *extension_);
1261 }
1262
1263 void ResetMediaRouteProvider() {
1264 binding_.reset();
1265 media_router_->BindToMojoRequest(mojo::GetProxy(&media_router_proxy_),
1266 *extension_);
1267 }
1268
1269 void RegisterMediaRouteProvider() {
1270 media_router_proxy_->RegisterMediaRouteProvider(
1271 std::move(media_route_provider_proxy_),
1272 base::Bind(&RegisterMediaRouteProviderHandler::Invoke,
1273 base::Unretained(&provide_handler_)));
1274 }
1275
1276 void ProcessEventLoop() {
1277 message_loop_.RunUntilIdle();
1278 }
1279
1280 void ExpectWakeReasonBucketCount(MediaRouteProviderWakeReason reason,
1281 int expected_count) {
1282 histogram_tester_.ExpectBucketCount("MediaRouter.Provider.WakeReason",
1283 static_cast<int>(reason),
1284 expected_count);
1285 }
1286
1287 void ExpectVersionBucketCount(MediaRouteProviderVersion version,
1288 int expected_count) {
1289 histogram_tester_.ExpectBucketCount("MediaRouter.Provider.Version",
1290 static_cast<int>(version),
1291 expected_count);
1292 }
1293
1294 void ExpectWakeupBucketCount(MediaRouteProviderWakeup wakeup,
1295 int expected_count) {
1296 histogram_tester_.ExpectBucketCount("MediaRouter.Provider.Wakeup",
1297 static_cast<int>(wakeup),
1298 expected_count);
1299 }
1300
1301 scoped_ptr<MediaRouterMojoImpl> media_router_;
1302 RegisterMediaRouteProviderHandler provide_handler_;
1303 TestProcessManager* process_manager_;
1304 testing::StrictMock<MockMediaRouteProvider> mock_media_route_provider_;
1305 interfaces::MediaRouterPtr media_router_proxy_;
1306 scoped_refptr<extensions::Extension> extension_;
1307
1308 private:
1309 scoped_ptr<TestingProfile> profile_;
1310 base::MessageLoop message_loop_;
1311 interfaces::MediaRouteProviderPtr media_route_provider_proxy_;
1312 scoped_ptr<mojo::Binding<interfaces::MediaRouteProvider>> binding_;
1313 base::HistogramTester histogram_tester_;
1314
1315 DISALLOW_COPY_AND_ASSIGN(MediaRouterMojoExtensionTest);
1316 };
1317
1318 TEST_F(MediaRouterMojoExtensionTest, DeferredBindingAndSuspension) {
1319 // DetachRoute is called before *any* extension has connected.
1320 // It should be queued.
1321 media_router_->DetachRoute(kRouteId);
1322
1323 BindMediaRouteProvider();
1324
1325 base::RunLoop run_loop, run_loop2;
1326 // |mojo_media_router| signals its readiness to the MR by registering
1327 // itself via RegisterMediaRouteProvider().
1328 // Now that the |media_router| and |mojo_media_router| are fully initialized,
1329 // the queued DetachRoute() call should be executed.
1330 EXPECT_CALL(provide_handler_, Invoke(testing::Not("")))
1331 .WillOnce(InvokeWithoutArgs([&run_loop]() {
1332 run_loop.Quit();
1333 }));
1334 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1335 .WillOnce(Return(false));
1336 EXPECT_CALL(mock_media_route_provider_, DetachRoute(mojo::String(kRouteId)))
1337 .WillOnce(InvokeWithoutArgs([&run_loop2]() {
1338 run_loop2.Quit();
1339 }));
1340 RegisterMediaRouteProvider();
1341 run_loop.Run();
1342 run_loop2.Run();
1343
1344 base::RunLoop run_loop3;
1345 // Extension is suspended and re-awoken.
1346 ResetMediaRouteProvider();
1347 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1348 .WillOnce(Return(true));
1349 EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
1350 .WillOnce(testing::DoAll(
1351 media::RunCallback<1>(true),
1352 InvokeWithoutArgs([&run_loop3]() { run_loop3.Quit(); }),
1353 Return(true)));
1354 media_router_->DetachRoute(kRouteId2);
1355 run_loop3.Run();
1356
1357 base::RunLoop run_loop4, run_loop5;
1358 // RegisterMediaRouteProvider() is called.
1359 // The queued DetachRoute(kRouteId2) call should be executed.
1360 EXPECT_CALL(provide_handler_, Invoke(testing::Not("")))
1361 .WillOnce(InvokeWithoutArgs([&run_loop4]() {
1362 run_loop4.Quit();
1363 }));
1364 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1365 .WillOnce(Return(false));
1366 EXPECT_CALL(mock_media_route_provider_, DetachRoute(mojo::String(kRouteId2)))
1367 .WillOnce(InvokeWithoutArgs([&run_loop5]() {
1368 run_loop5.Quit();
1369 }));
1370 BindMediaRouteProvider();
1371 RegisterMediaRouteProvider();
1372 run_loop4.Run();
1373 run_loop5.Run();
1374 ExpectWakeReasonBucketCount(MediaRouteProviderWakeReason::DETACH_ROUTE, 1);
1375 ExpectWakeupBucketCount(MediaRouteProviderWakeup::SUCCESS, 1);
1376 ExpectVersionBucketCount(MediaRouteProviderVersion::SAME_VERSION_AS_CHROME,
1377 1);
1378 }
1379
1380 TEST_F(MediaRouterMojoExtensionTest, AttemptedWakeupTooManyTimes) {
1381 BindMediaRouteProvider();
1382
1383 // DetachRoute is called while extension is suspended. It should be queued.
1384 // Schedule a component extension wakeup.
1385 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1386 .WillOnce(Return(true));
1387 EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
1388 .WillOnce(testing::DoAll(media::RunCallback<1>(true), Return(true)));
1389 media_router_->DetachRoute(kRouteId);
1390 EXPECT_EQ(1u, media_router_->pending_requests_.size());
1391 ExpectWakeReasonBucketCount(MediaRouteProviderWakeReason::DETACH_ROUTE, 1);
1392 ExpectWakeupBucketCount(MediaRouteProviderWakeup::SUCCESS, 1);
1393
1394 // Media route provider fails to connect to media router before extension is
1395 // suspended again, and |OnConnectionError| is invoked. Retry the wakeup.
1396 EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
1397 .Times(MediaRouterMojoImpl::kMaxWakeupAttemptCount - 1)
1398 .WillRepeatedly(
1399 testing::DoAll(media::RunCallback<1>(true), Return(true)));
1400 for (int i = 0; i < MediaRouterMojoImpl::kMaxWakeupAttemptCount - 1; ++i)
1401 media_router_->OnConnectionError();
1402
1403 // We have already tried |kMaxWakeupAttemptCount| times. If we get an error
1404 // again, we will give up and the pending request queue will be drained.
1405 media_router_->OnConnectionError();
1406 EXPECT_TRUE(media_router_->pending_requests_.empty());
1407 ExpectWakeReasonBucketCount(MediaRouteProviderWakeReason::CONNECTION_ERROR,
1408 MediaRouterMojoImpl::kMaxWakeupAttemptCount - 1);
1409 ExpectWakeupBucketCount(MediaRouteProviderWakeup::ERROR_TOO_MANY_RETRIES, 1);
1410
1411 // Requests that comes in after queue is drained should be queued.
1412 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1413 .WillOnce(Return(true));
1414 EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
1415 .WillOnce(testing::DoAll(media::RunCallback<1>(true), Return(true)));
1416 media_router_->DetachRoute(kRouteId);
1417 EXPECT_EQ(1u, media_router_->pending_requests_.size());
1418 ExpectVersionBucketCount(MediaRouteProviderVersion::SAME_VERSION_AS_CHROME,
1419 1);
1420 }
1421
1422 TEST_F(MediaRouterMojoExtensionTest, WakeupFailedDrainsQueue) {
1423 BindMediaRouteProvider();
1424
1425 // DetachRoute is called while extension is suspended. It should be queued.
1426 // Schedule a component extension wakeup.
1427 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1428 .WillOnce(Return(true));
1429 base::Callback<void(bool)> extension_wakeup_callback;
1430 EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
1431 .WillOnce(
1432 testing::DoAll(SaveArg<1>(&extension_wakeup_callback), Return(true)));
1433 media_router_->DetachRoute(kRouteId);
1434 EXPECT_EQ(1u, media_router_->pending_requests_.size());
1435
1436 // Extension wakeup callback returning false is an non-retryable error.
1437 // Queue should be drained.
1438 extension_wakeup_callback.Run(false);
1439 EXPECT_TRUE(media_router_->pending_requests_.empty());
1440
1441 // Requests that comes in after queue is drained should be queued.
1442 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()))
1443 .WillOnce(Return(true));
1444 EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
1445 .WillOnce(testing::DoAll(media::RunCallback<1>(true), Return(true)));
1446 media_router_->DetachRoute(kRouteId);
1447 EXPECT_EQ(1u, media_router_->pending_requests_.size());
1448 ExpectWakeReasonBucketCount(MediaRouteProviderWakeReason::DETACH_ROUTE, 1);
1449 ExpectWakeupBucketCount(MediaRouteProviderWakeup::ERROR_UNKNOWN, 1);
1450 ExpectVersionBucketCount(MediaRouteProviderVersion::SAME_VERSION_AS_CHROME,
1451 1);
1452 }
1453
1454 TEST_F(MediaRouterMojoExtensionTest, DropOldestPendingRequest) {
1455 const size_t kMaxPendingRequests = MediaRouterMojoImpl::kMaxPendingRequests;
1456
1457 // Request is queued.
1458 media_router_->DetachRoute(kRouteId);
1459 EXPECT_EQ(1u, media_router_->pending_requests_.size());
1460
1461 for (size_t i = 0; i < kMaxPendingRequests; ++i)
1462 media_router_->DetachRoute(kRouteId2);
1463
1464 // The request queue size should not exceed |kMaxPendingRequests|.
1465 EXPECT_EQ(kMaxPendingRequests, media_router_->pending_requests_.size());
1466
1467 base::RunLoop run_loop, run_loop2;
1468 size_t count = 0;
1469 // The oldest request should have been dropped, so we don't expect to see
1470 // DetachRoute(kRouteId) here.
1471 BindMediaRouteProvider();
1472 EXPECT_CALL(provide_handler_, Invoke(testing::Not("")))
1473 .WillOnce(InvokeWithoutArgs([&run_loop]() {
1474 run_loop.Quit();
1475 }));
1476 EXPECT_CALL(*process_manager_, IsEventPageSuspended(extension_->id()));
1477 EXPECT_CALL(mock_media_route_provider_, DetachRoute(mojo::String(kRouteId2)))
1478 .Times(kMaxPendingRequests)
1479 .WillRepeatedly(InvokeWithoutArgs([&run_loop2, &count]() {
1480 if (++count == MediaRouterMojoImpl::kMaxPendingRequests)
1481 run_loop2.Quit();
1482 }));
1483 RegisterMediaRouteProvider();
1484 run_loop.Run();
1485 run_loop2.Run();
1486 ExpectVersionBucketCount(MediaRouteProviderVersion::SAME_VERSION_AS_CHROME,
1487 1);
1488 }
1489
1490 } // namespace media_router
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698