OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 "chrome/browser/media/cast_remoting_connector.h" |
| 6 |
| 7 #include <utility> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/callback.h" |
| 11 #include "base/memory/weak_ptr.h" |
| 12 #include "base/run_loop.h" |
| 13 #include "chrome/browser/media/router/media_route.h" |
| 14 #include "chrome/browser/media/router/media_routes_observer.h" |
| 15 #include "chrome/browser/media/router/media_source.h" |
| 16 #include "chrome/browser/media/router/mock_media_router.h" |
| 17 #include "chrome/browser/media/router/route_message.h" |
| 18 #include "chrome/browser/media/router/route_message_observer.h" |
| 19 #include "content/public/browser/browser_thread.h" |
| 20 #include "content/public/test/test_browser_thread_bundle.h" |
| 21 #include "media/mojo/interfaces/remoting.mojom.h" |
| 22 #include "mojo/public/cpp/bindings/binding.h" |
| 23 #include "testing/gmock/include/gmock/gmock.h" |
| 24 #include "testing/gtest/include/gtest/gtest.h" |
| 25 |
| 26 using content::BrowserThread; |
| 27 |
| 28 using media::mojom::RemoterPtr; |
| 29 using media::mojom::RemoterRequest; |
| 30 using media::mojom::RemotingSourcePtr; |
| 31 using media::mojom::RemotingSourceRequest; |
| 32 using media::mojom::RemotingStartFailReason; |
| 33 using media::mojom::RemotingStopReason; |
| 34 |
| 35 using media_router::MediaRoutesObserver; |
| 36 using media_router::MediaRoute; |
| 37 using media_router::MediaSource; |
| 38 using media_router::RouteMessage; |
| 39 using media_router::RouteMessageObserver; |
| 40 |
| 41 using ::testing::_; |
| 42 using ::testing::AtLeast; |
| 43 |
| 44 namespace { |
| 45 |
| 46 constexpr char kRemotingMediaSource[] = |
| 47 "urn:x-org.chromium.media:source:tab_content_remoting:123"; |
| 48 constexpr char kRemotingMediaSink[] = "wiggles"; |
| 49 constexpr char kRemotingMediaRoute[] = |
| 50 "urn:x-org.chromium:media:route:garbly_gook_ssi7m4oa8oma7rasd/cast-wiggles"; |
| 51 |
| 52 // Implements basic functionality of a subset of the MediaRouter for use by the |
| 53 // unit tests in this module. Note that MockMediaRouter will complain at runtime |
| 54 // if any methods were called that should not have been called. |
| 55 class FakeMediaRouter : public media_router::MockMediaRouter { |
| 56 public: |
| 57 FakeMediaRouter() |
| 58 : routes_observer_(nullptr), message_observer_(nullptr), |
| 59 weak_factory_(this) {} |
| 60 ~FakeMediaRouter() final {} |
| 61 |
| 62 // |
| 63 // These methods are called by test code to create/destroy a media route and |
| 64 // pass messages in both directions. |
| 65 // |
| 66 |
| 67 void OnRemotingRouteExists(bool exists) { |
| 68 routes_.clear(); |
| 69 if (exists) { |
| 70 routes_.push_back(MediaRoute( |
| 71 kRemotingMediaRoute, MediaSource(routes_observer_->source_id()), |
| 72 kRemotingMediaSink, "Cast Media Remoting", false, "", false)); |
| 73 } else { |
| 74 // Cancel delivery of all messages in both directions. |
| 75 inbound_messages_.clear(); |
| 76 for (const auto& entry : outbound_messages_) { |
| 77 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 78 base::Bind(entry.second, false)); |
| 79 } |
| 80 outbound_messages_.clear(); |
| 81 } |
| 82 BrowserThread::PostTask( |
| 83 BrowserThread::UI, FROM_HERE, |
| 84 base::Bind(&FakeMediaRouter::DoUpdateRoutes, |
| 85 weak_factory_.GetWeakPtr())); |
| 86 } |
| 87 |
| 88 void OnMessageFromProvider(const std::string& message) { |
| 89 inbound_messages_.push_back(RouteMessage()); |
| 90 inbound_messages_.back().type = RouteMessage::TEXT; |
| 91 inbound_messages_.back().text = message; |
| 92 BrowserThread::PostTask( |
| 93 BrowserThread::UI, FROM_HERE, |
| 94 base::Bind(&FakeMediaRouter::DoDeliverInboundMessages, |
| 95 weak_factory_.GetWeakPtr())); |
| 96 } |
| 97 |
| 98 void OnBinaryMessageFromProvider(const std::vector<uint8_t>& message) { |
| 99 inbound_messages_.push_back(RouteMessage()); |
| 100 inbound_messages_.back().type = RouteMessage::BINARY; |
| 101 inbound_messages_.back().binary = std::vector<uint8_t>(message); |
| 102 BrowserThread::PostTask( |
| 103 BrowserThread::UI, FROM_HERE, |
| 104 base::Bind(&FakeMediaRouter::DoDeliverInboundMessages, |
| 105 weak_factory_.GetWeakPtr())); |
| 106 } |
| 107 |
| 108 void TakeMessagesSentToProvider(RouteMessage::Type type, |
| 109 std::vector<RouteMessage>* messages) { |
| 110 decltype(outbound_messages_) untaken_messages; |
| 111 for (const auto& entry : outbound_messages_) { |
| 112 if (entry.first.type == type) { |
| 113 messages->push_back(entry.first); |
| 114 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 115 base::Bind(entry.second, true)); |
| 116 } else { |
| 117 untaken_messages.push_back(entry); |
| 118 } |
| 119 } |
| 120 outbound_messages_.swap(untaken_messages); |
| 121 } |
| 122 |
| 123 protected: |
| 124 void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) final { |
| 125 CHECK(!routes_observer_); |
| 126 routes_observer_ = observer; |
| 127 CHECK(routes_observer_); |
| 128 BrowserThread::PostTask( |
| 129 BrowserThread::UI, FROM_HERE, |
| 130 base::Bind(&FakeMediaRouter::DoUpdateRoutes, |
| 131 weak_factory_.GetWeakPtr())); |
| 132 } |
| 133 |
| 134 void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) final { |
| 135 CHECK_EQ(routes_observer_, observer); |
| 136 routes_observer_ = nullptr; |
| 137 } |
| 138 |
| 139 void RegisterRouteMessageObserver(RouteMessageObserver* observer) final { |
| 140 CHECK(!message_observer_); |
| 141 message_observer_ = observer; |
| 142 CHECK(message_observer_); |
| 143 } |
| 144 |
| 145 void UnregisterRouteMessageObserver(RouteMessageObserver* observer) final { |
| 146 CHECK_EQ(message_observer_, observer); |
| 147 message_observer_ = nullptr; |
| 148 } |
| 149 |
| 150 void SendRouteMessage(const MediaRoute::Id& route_id, |
| 151 const std::string& text, |
| 152 const SendRouteMessageCallback& callback) final { |
| 153 EXPECT_EQ(message_observer_->route_id(), route_id); |
| 154 ASSERT_FALSE(callback.is_null()); |
| 155 RouteMessage message; |
| 156 message.type = RouteMessage::TEXT; |
| 157 message.text = text; |
| 158 outbound_messages_.push_back(std::make_pair(message, callback)); |
| 159 } |
| 160 |
| 161 void SendRouteBinaryMessage( |
| 162 const MediaRoute::Id& route_id, |
| 163 std::unique_ptr<std::vector<uint8_t>> data, |
| 164 const SendRouteMessageCallback& callback) final { |
| 165 EXPECT_EQ(message_observer_->route_id(), route_id); |
| 166 ASSERT_TRUE(!!data); |
| 167 ASSERT_FALSE(callback.is_null()); |
| 168 RouteMessage message; |
| 169 message.type = RouteMessage::BINARY; |
| 170 message.binary = std::move(*data); |
| 171 outbound_messages_.push_back(std::make_pair(message, callback)); |
| 172 } |
| 173 |
| 174 private: |
| 175 // Asynchronous callback to notify the MediaRoutesObserver of a change in |
| 176 // routes. |
| 177 void DoUpdateRoutes() { |
| 178 if (routes_observer_) |
| 179 routes_observer_->OnRoutesUpdated(routes_, std::vector<MediaRoute::Id>()); |
| 180 } |
| 181 |
| 182 // Asynchronous callback to deliver messages to the RouteMessageObserver. |
| 183 void DoDeliverInboundMessages() { |
| 184 if (message_observer_) |
| 185 message_observer_->OnMessagesReceived(inbound_messages_); |
| 186 inbound_messages_.clear(); |
| 187 } |
| 188 |
| 189 MediaRoutesObserver* routes_observer_; |
| 190 RouteMessageObserver* message_observer_; |
| 191 |
| 192 std::vector<MediaRoute> routes_; |
| 193 // Messages from Cast Provider to the connector. |
| 194 std::vector<RouteMessage> inbound_messages_; |
| 195 // Messages from the connector to the Cast Provider. |
| 196 using OutboundMessageAndCallback = |
| 197 std::pair<RouteMessage, SendRouteMessageCallback>; |
| 198 std::vector<OutboundMessageAndCallback> outbound_messages_; |
| 199 |
| 200 base::WeakPtrFactory<FakeMediaRouter> weak_factory_; |
| 201 }; |
| 202 |
| 203 class MockRemotingSource : public media::mojom::RemotingSource { |
| 204 public: |
| 205 MockRemotingSource() : binding_(this) {} |
| 206 ~MockRemotingSource() final {} |
| 207 |
| 208 void Bind(RemotingSourceRequest request) { |
| 209 binding_.Bind(std::move(request)); |
| 210 } |
| 211 |
| 212 MOCK_METHOD0(OnSinkAvailable, void()); |
| 213 MOCK_METHOD0(OnSinkGone, void()); |
| 214 MOCK_METHOD0(OnStarted, void()); |
| 215 MOCK_METHOD1(OnStartFailed, void(RemotingStartFailReason)); |
| 216 MOCK_METHOD1(OnMessageFromSink, void(const std::vector<uint8_t>&)); |
| 217 MOCK_METHOD1(OnStopped, void(RemotingStopReason)); |
| 218 |
| 219 private: |
| 220 mojo::Binding<media::mojom::RemotingSource> binding_; |
| 221 }; |
| 222 |
| 223 } // namespace |
| 224 |
| 225 class CastRemotingConnectorTest : public ::testing::Test { |
| 226 public: |
| 227 CastRemotingConnectorTest() |
| 228 : remoting_source_(kRemotingMediaSource), |
| 229 connector_(&media_router_, remoting_source_.id()) {} |
| 230 |
| 231 void TearDown() final { |
| 232 // Allow any pending Mojo operations to complete before destruction. For |
| 233 // example, when one end of a Mojo message pipe is closed, a task is posted |
| 234 // to later destroy objects that were owned by the message pipe. |
| 235 RunUntilIdle(); |
| 236 } |
| 237 |
| 238 protected: |
| 239 RemoterPtr CreateRemoter(MockRemotingSource* source) { |
| 240 RemotingSourcePtr source_ptr; |
| 241 source->Bind(mojo::GetProxy(&source_ptr)); |
| 242 RemoterPtr remoter_ptr; |
| 243 connector_.CreateBridge(std::move(source_ptr), |
| 244 mojo::GetProxy(&remoter_ptr)); |
| 245 return remoter_ptr; |
| 246 } |
| 247 |
| 248 void ProviderDiscoversSink() { |
| 249 media_router_.OnRemotingRouteExists(true); |
| 250 } |
| 251 |
| 252 void ProviderLosesSink() { |
| 253 media_router_.OnRemotingRouteExists(false); |
| 254 } |
| 255 |
| 256 void ConnectorSentMessageToProvider(const std::string& expected_message) { |
| 257 std::vector<RouteMessage> messages; |
| 258 media_router_.TakeMessagesSentToProvider(RouteMessage::TEXT, &messages); |
| 259 bool did_see_expected_message = false; |
| 260 for (const RouteMessage& message : messages) { |
| 261 if (message.text && expected_message == *message.text) { |
| 262 did_see_expected_message = true; |
| 263 } else { |
| 264 ADD_FAILURE() |
| 265 << "Unexpected message: " << message.ToHumanReadableString(); |
| 266 } |
| 267 } |
| 268 EXPECT_TRUE(did_see_expected_message); |
| 269 } |
| 270 |
| 271 void ConnectorSentMessageToSink( |
| 272 const std::vector<uint8_t>& expected_message) { |
| 273 std::vector<RouteMessage> messages; |
| 274 media_router_.TakeMessagesSentToProvider(RouteMessage::BINARY, &messages); |
| 275 bool did_see_expected_message = false; |
| 276 for (const RouteMessage& message : messages) { |
| 277 if (message.binary && expected_message == *message.binary) { |
| 278 did_see_expected_message = true; |
| 279 } else { |
| 280 ADD_FAILURE() |
| 281 << "Unexpected message: " << message.ToHumanReadableString(); |
| 282 } |
| 283 } |
| 284 EXPECT_TRUE(did_see_expected_message); |
| 285 } |
| 286 |
| 287 void ConnectorSentNoMessagesToProvider() { |
| 288 std::vector<RouteMessage> messages; |
| 289 media_router_.TakeMessagesSentToProvider(RouteMessage::TEXT, &messages); |
| 290 EXPECT_TRUE(messages.empty()); |
| 291 } |
| 292 |
| 293 void ConnectorSentNoMessagesToSink() { |
| 294 std::vector<RouteMessage> messages; |
| 295 media_router_.TakeMessagesSentToProvider(RouteMessage::BINARY, &messages); |
| 296 EXPECT_TRUE(messages.empty()); |
| 297 } |
| 298 |
| 299 void ProviderPassesMessageFromSink( |
| 300 const std::vector<uint8_t>& message) { |
| 301 media_router_.OnBinaryMessageFromProvider(message); |
| 302 } |
| 303 |
| 304 void ProviderSaysToRemotingConnector(const std::string& message) { |
| 305 media_router_.OnMessageFromProvider(message); |
| 306 } |
| 307 |
| 308 void MediaRouterTerminatesRoute() { |
| 309 media_router_.OnRemotingRouteExists(false); |
| 310 } |
| 311 |
| 312 static void RunUntilIdle() { |
| 313 base::RunLoop().RunUntilIdle(); |
| 314 } |
| 315 |
| 316 private: |
| 317 content::TestBrowserThreadBundle browser_thread_bundle_; |
| 318 FakeMediaRouter media_router_; |
| 319 const MediaSource remoting_source_; |
| 320 CastRemotingConnector connector_; |
| 321 }; |
| 322 |
| 323 TEST_F(CastRemotingConnectorTest, NeverNotifiesThatSinkIsAvailable) { |
| 324 MockRemotingSource source; |
| 325 RemoterPtr remoter = CreateRemoter(&source); |
| 326 |
| 327 EXPECT_CALL(source, OnSinkAvailable()).Times(0); |
| 328 EXPECT_CALL(source, OnSinkGone()).Times(AtLeast(0)); |
| 329 RunUntilIdle(); |
| 330 } |
| 331 |
| 332 TEST_F(CastRemotingConnectorTest, NotifiesWhenSinkIsAvailableAndThenGone) { |
| 333 MockRemotingSource source; |
| 334 RemoterPtr remoter = CreateRemoter(&source); |
| 335 |
| 336 EXPECT_CALL(source, OnSinkAvailable()).Times(1); |
| 337 ProviderDiscoversSink(); |
| 338 RunUntilIdle(); |
| 339 |
| 340 EXPECT_CALL(source, OnSinkGone()).Times(AtLeast(1)); |
| 341 ProviderLosesSink(); |
| 342 RunUntilIdle(); |
| 343 } |
| 344 |
| 345 TEST_F(CastRemotingConnectorTest, |
| 346 NotifiesMultipleSourcesWhenSinkIsAvailableAndThenGone) { |
| 347 MockRemotingSource source1; |
| 348 RemoterPtr remoter1 = CreateRemoter(&source1); |
| 349 MockRemotingSource source2; |
| 350 RemoterPtr remoter2 = CreateRemoter(&source2); |
| 351 |
| 352 EXPECT_CALL(source1, OnSinkAvailable()).Times(1); |
| 353 EXPECT_CALL(source2, OnSinkAvailable()).Times(1); |
| 354 ProviderDiscoversSink(); |
| 355 RunUntilIdle(); |
| 356 |
| 357 EXPECT_CALL(source1, OnSinkGone()).Times(AtLeast(1)); |
| 358 EXPECT_CALL(source2, OnSinkGone()).Times(AtLeast(1)); |
| 359 ProviderLosesSink(); |
| 360 RunUntilIdle(); |
| 361 } |
| 362 |
| 363 TEST_F(CastRemotingConnectorTest, HandlesTeardownOfRemotingSourceFirst) { |
| 364 std::unique_ptr<MockRemotingSource> source(new MockRemotingSource); |
| 365 RemoterPtr remoter = CreateRemoter(source.get()); |
| 366 |
| 367 EXPECT_CALL(*source, OnSinkAvailable()).Times(1); |
| 368 ProviderDiscoversSink(); |
| 369 RunUntilIdle(); |
| 370 |
| 371 source.reset(); |
| 372 RunUntilIdle(); |
| 373 } |
| 374 |
| 375 TEST_F(CastRemotingConnectorTest, HandlesTeardownOfRemoterFirst) { |
| 376 MockRemotingSource source; |
| 377 RemoterPtr remoter = CreateRemoter(&source); |
| 378 |
| 379 EXPECT_CALL(source, OnSinkAvailable()).Times(1); |
| 380 ProviderDiscoversSink(); |
| 381 RunUntilIdle(); |
| 382 |
| 383 remoter.reset(); |
| 384 RunUntilIdle(); |
| 385 } |
| 386 |
| 387 namespace { |
| 388 |
| 389 // The possible ways a remoting session may be terminated in the "full |
| 390 // run-through" tests. |
| 391 enum HowItEnds { |
| 392 SOURCE_TERMINATES, // The render process decides to end remoting. |
| 393 MOJO_PIPE_CLOSES, // A Mojo message pipe closes unexpectedly. |
| 394 ROUTE_TERMINATES, // The Media Router UI was used to terminate the route. |
| 395 EXTERNAL_FAILURE, // The sink is cut-off, perhaps due to a network outage. |
| 396 }; |
| 397 |
| 398 } // namespace |
| 399 |
| 400 class CastRemotingConnectorFullSessionTest |
| 401 : public CastRemotingConnectorTest, |
| 402 public ::testing::WithParamInterface<HowItEnds> { |
| 403 public: |
| 404 HowItEnds how_it_ends() const { return GetParam(); } |
| 405 }; |
| 406 |
| 407 // Performs a full run-through of starting and stopping remoting, with |
| 408 // communications between source and sink established at the correct times, and |
| 409 // tests that end-to-end behavior is correct depending on what caused the |
| 410 // remoting session to end. |
| 411 TEST_P(CastRemotingConnectorFullSessionTest, GoesThroughAllTheMotions) { |
| 412 std::unique_ptr<MockRemotingSource> source(new MockRemotingSource()); |
| 413 RemoterPtr remoter = CreateRemoter(source.get()); |
| 414 std::unique_ptr<MockRemotingSource> other_source(new MockRemotingSource()); |
| 415 RemoterPtr other_remoter = CreateRemoter(other_source.get()); |
| 416 |
| 417 // Throughout this test |other_source| should not participate in the |
| 418 // remoting session, and so these method calls should never occur: |
| 419 EXPECT_CALL(*other_source, OnStarted()).Times(0); |
| 420 EXPECT_CALL(*other_source, OnStopped(_)).Times(0); |
| 421 EXPECT_CALL(*other_source, OnMessageFromSink(_)).Times(0); |
| 422 |
| 423 // Both sinks should be notified when the Cast Provider tells the connector |
| 424 // a remoting sink is available. |
| 425 EXPECT_CALL(*source, OnSinkAvailable()).Times(1).RetiresOnSaturation(); |
| 426 EXPECT_CALL(*other_source, OnSinkAvailable()).Times(1).RetiresOnSaturation(); |
| 427 ProviderDiscoversSink(); |
| 428 RunUntilIdle(); |
| 429 |
| 430 // When |source| starts a remoting session, |other_source| is notified the |
| 431 // sink is gone, the Cast Provider is notified that remoting has started, |
| 432 // and |source| is notified that its request was successful. |
| 433 EXPECT_CALL(*source, OnStarted()).Times(1).RetiresOnSaturation(); |
| 434 EXPECT_CALL(*other_source, OnSinkGone()).Times(1).RetiresOnSaturation(); |
| 435 remoter->Start(); |
| 436 RunUntilIdle(); |
| 437 ConnectorSentMessageToProvider("START_CAST_REMOTING:session=1"); |
| 438 |
| 439 // The |source| should now be able to send binary messages to the sink. |
| 440 // |other_source| should not! |
| 441 const std::vector<uint8_t> message_to_sink = { 3, 1, 4, 1, 5, 9 }; |
| 442 remoter->SendMessageToSink(message_to_sink); |
| 443 const std::vector<uint8_t> ignored_message_to_sink = { 1, 2, 3 }; |
| 444 other_remoter->SendMessageToSink(ignored_message_to_sink); |
| 445 RunUntilIdle(); |
| 446 ConnectorSentMessageToSink(message_to_sink); |
| 447 |
| 448 // The sink should also be able to send binary messages to the |source|. |
| 449 const std::vector<uint8_t> message_to_source = { 2, 7, 1, 8, 2, 8 }; |
| 450 EXPECT_CALL(*source, OnMessageFromSink(message_to_source)).Times(1) |
| 451 .RetiresOnSaturation(); |
| 452 ProviderPassesMessageFromSink(message_to_source); |
| 453 RunUntilIdle(); |
| 454 |
| 455 // The |other_source| should not be allowed to start a remoting session. |
| 456 EXPECT_CALL(*other_source, |
| 457 OnStartFailed(RemotingStartFailReason::CANNOT_START_MULTIPLE)) |
| 458 .Times(1).RetiresOnSaturation(); |
| 459 other_remoter->Start(); |
| 460 RunUntilIdle(); |
| 461 ConnectorSentNoMessagesToProvider(); |
| 462 |
| 463 // What happens from here depends on how this remoting session will end... |
| 464 switch (how_it_ends()) { |
| 465 case SOURCE_TERMINATES: { |
| 466 // When the |source| stops the remoting session, the Cast Provider is |
| 467 // notified the session has stopped, and the |source| receives both an |
| 468 // OnStopped() and an OnSinkGone() notification. |
| 469 const RemotingStopReason reason = RemotingStopReason::LOCAL_PLAYBACK; |
| 470 EXPECT_CALL(*source, OnSinkGone()).Times(1).RetiresOnSaturation(); |
| 471 EXPECT_CALL(*source, OnStopped(reason)).Times(1).RetiresOnSaturation(); |
| 472 remoter->Stop(reason); |
| 473 RunUntilIdle(); |
| 474 ConnectorSentMessageToProvider("STOP_CAST_REMOTING:session=1"); |
| 475 |
| 476 // Since remoting is stopped, any further messaging in either direction |
| 477 // must be dropped. |
| 478 const std::vector<uint8_t> message_to_sink = { 1, 6, 1, 8, 0, 3 }; |
| 479 const std::vector<uint8_t> message_to_source = { 6, 2, 8, 3, 1, 8 }; |
| 480 EXPECT_CALL(*source, OnMessageFromSink(_)).Times(0); |
| 481 remoter->SendMessageToSink(message_to_sink); |
| 482 ProviderPassesMessageFromSink(message_to_source); |
| 483 RunUntilIdle(); |
| 484 ConnectorSentNoMessagesToSink(); |
| 485 |
| 486 // When the sink is ready, the Cast Provider sends a notification to the |
| 487 // connector. The connector will notify both sources that a sink is once |
| 488 // again available. |
| 489 EXPECT_CALL(*source, OnSinkAvailable()).Times(1).RetiresOnSaturation(); |
| 490 EXPECT_CALL(*other_source, OnSinkAvailable()).Times(1) |
| 491 .RetiresOnSaturation(); |
| 492 ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1"); |
| 493 RunUntilIdle(); |
| 494 |
| 495 // When the sink is no longer available, the Cast Provider notifies the |
| 496 // connector, and both sources are then notified the sink is gone. |
| 497 EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1)); |
| 498 EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(1)); |
| 499 ProviderLosesSink(); |
| 500 RunUntilIdle(); |
| 501 |
| 502 break; |
| 503 } |
| 504 |
| 505 case MOJO_PIPE_CLOSES: |
| 506 // When the Mojo pipes for |other_source| close, this should not affect |
| 507 // the current remoting session. |
| 508 other_source.reset(); |
| 509 other_remoter.reset(); |
| 510 RunUntilIdle(); |
| 511 ConnectorSentNoMessagesToProvider(); |
| 512 |
| 513 // Now, when the Mojo pipes for |source| close, the Cast Provider will be |
| 514 // notified that the session has stopped. |
| 515 source.reset(); |
| 516 remoter.reset(); |
| 517 RunUntilIdle(); |
| 518 ConnectorSentMessageToProvider("STOP_CAST_REMOTING:session=1"); |
| 519 |
| 520 // The Cast Provider will detect when the sink is ready for the next |
| 521 // remoting session, and then notify the connector. However, there are no |
| 522 // sources to propagate this notification to. |
| 523 ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1"); |
| 524 RunUntilIdle(); |
| 525 |
| 526 break; |
| 527 |
| 528 case ROUTE_TERMINATES: |
| 529 // When the Media Router terminates the route (e.g., because a user |
| 530 // terminated the route from the UI), the source and sink are immediately |
| 531 // cut off from one another. |
| 532 EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1)); |
| 533 EXPECT_CALL(*source, OnStopped(RemotingStopReason::ROUTE_TERMINATED)) |
| 534 .Times(1).RetiresOnSaturation(); |
| 535 EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(0)); |
| 536 MediaRouterTerminatesRoute(); |
| 537 RunUntilIdle(); |
| 538 ConnectorSentNoMessagesToProvider(); |
| 539 |
| 540 // Furthermore, the connector and Cast Provider are also cut off from one |
| 541 // another and should not be able to exchange messages anymore. Therefore, |
| 542 // the connector will never try to notify the sources that the sink is |
| 543 // available again. |
| 544 EXPECT_CALL(*source, OnSinkAvailable()).Times(0); |
| 545 EXPECT_CALL(*other_source, OnSinkAvailable()).Times(0); |
| 546 ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1"); |
| 547 RunUntilIdle(); |
| 548 |
| 549 break; |
| 550 |
| 551 case EXTERNAL_FAILURE: { |
| 552 // When the Cast Provider is cut-off from the sink, it sends a fail |
| 553 // notification to the connector. The connector, in turn, force-stops the |
| 554 // remoting session and notifies the |source|. |
| 555 EXPECT_CALL(*source, OnSinkGone()).Times(1).RetiresOnSaturation(); |
| 556 EXPECT_CALL(*source, OnStopped(RemotingStopReason::UNEXPECTED_FAILURE)) |
| 557 .Times(1).RetiresOnSaturation(); |
| 558 ProviderSaysToRemotingConnector("FAILED_CAST_REMOTING:session=1"); |
| 559 RunUntilIdle(); |
| 560 |
| 561 // Since remoting is stopped, any further messaging in either direction |
| 562 // must be dropped. |
| 563 const std::vector<uint8_t> message_to_sink = { 1, 6, 1, 8, 0, 3 }; |
| 564 const std::vector<uint8_t> message_to_source = { 6, 2, 8, 3, 1, 8 }; |
| 565 EXPECT_CALL(*source, OnMessageFromSink(_)).Times(0); |
| 566 remoter->SendMessageToSink(message_to_sink); |
| 567 ProviderPassesMessageFromSink(message_to_source); |
| 568 RunUntilIdle(); |
| 569 ConnectorSentNoMessagesToSink(); |
| 570 |
| 571 // Later, if whatever caused the external failure has resolved, the Cast |
| 572 // Provider will notify the connector that the sink is available one |
| 573 // again. |
| 574 EXPECT_CALL(*source, OnSinkAvailable()).Times(1).RetiresOnSaturation(); |
| 575 EXPECT_CALL(*other_source, OnSinkAvailable()).Times(1) |
| 576 .RetiresOnSaturation(); |
| 577 ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1"); |
| 578 RunUntilIdle(); |
| 579 |
| 580 // When the sink is no longer available, the Cast Provider notifies the |
| 581 // connector, and both sources are then notified the sink is gone. |
| 582 EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1)); |
| 583 EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(1)); |
| 584 ProviderLosesSink(); |
| 585 RunUntilIdle(); |
| 586 |
| 587 break; |
| 588 } |
| 589 } |
| 590 } |
| 591 |
| 592 INSTANTIATE_TEST_CASE_P(, CastRemotingConnectorFullSessionTest, |
| 593 ::testing::Values(SOURCE_TERMINATES, |
| 594 MOJO_PIPE_CLOSES, |
| 595 ROUTE_TERMINATES, |
| 596 EXTERNAL_FAILURE)); |
OLD | NEW |