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

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

Powered by Google App Engine
This is Rietveld 408576698