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

Unified Diff: chrome/browser/media/cast_remoting_connector_unittest.cc

Issue 2310753002: Media Remoting: Data/Control plumbing between renderer and Media Router. (Closed)
Patch Set: Just a REBASE on ToT before commit. Created 4 years, 3 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/media/cast_remoting_connector_unittest.cc
diff --git a/chrome/browser/media/cast_remoting_connector_unittest.cc b/chrome/browser/media/cast_remoting_connector_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..db27469abc7c5e76a62f743c3c955e774c28cea5
--- /dev/null
+++ b/chrome/browser/media/cast_remoting_connector_unittest.cc
@@ -0,0 +1,596 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/cast_remoting_connector.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "chrome/browser/media/router/media_route.h"
+#include "chrome/browser/media/router/media_routes_observer.h"
+#include "chrome/browser/media/router/media_source.h"
+#include "chrome/browser/media/router/mock_media_router.h"
+#include "chrome/browser/media/router/route_message.h"
+#include "chrome/browser/media/router/route_message_observer.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "media/mojo/interfaces/remoting.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+using media::mojom::RemoterPtr;
+using media::mojom::RemoterRequest;
+using media::mojom::RemotingSourcePtr;
+using media::mojom::RemotingSourceRequest;
+using media::mojom::RemotingStartFailReason;
+using media::mojom::RemotingStopReason;
+
+using media_router::MediaRoutesObserver;
+using media_router::MediaRoute;
+using media_router::MediaSource;
+using media_router::RouteMessage;
+using media_router::RouteMessageObserver;
+
+using ::testing::_;
+using ::testing::AtLeast;
+
+namespace {
+
+constexpr char kRemotingMediaSource[] =
+ "urn:x-org.chromium.media:source:tab_content_remoting:123";
+constexpr char kRemotingMediaSink[] = "wiggles";
+constexpr char kRemotingMediaRoute[] =
+ "urn:x-org.chromium:media:route:garbly_gook_ssi7m4oa8oma7rasd/cast-wiggles";
+
+// Implements basic functionality of a subset of the MediaRouter for use by the
+// unit tests in this module. Note that MockMediaRouter will complain at runtime
+// if any methods were called that should not have been called.
+class FakeMediaRouter : public media_router::MockMediaRouter {
+ public:
+ FakeMediaRouter()
+ : routes_observer_(nullptr), message_observer_(nullptr),
+ weak_factory_(this) {}
+ ~FakeMediaRouter() final {}
+
+ //
+ // These methods are called by test code to create/destroy a media route and
+ // pass messages in both directions.
+ //
+
+ void OnRemotingRouteExists(bool exists) {
+ routes_.clear();
+ if (exists) {
+ routes_.push_back(MediaRoute(
+ kRemotingMediaRoute, MediaSource(routes_observer_->source_id()),
+ kRemotingMediaSink, "Cast Media Remoting", false, "", false));
+ } else {
+ // Cancel delivery of all messages in both directions.
+ inbound_messages_.clear();
+ for (const auto& entry : outbound_messages_) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(entry.second, false));
+ }
+ outbound_messages_.clear();
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&FakeMediaRouter::DoUpdateRoutes,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void OnMessageFromProvider(const std::string& message) {
+ inbound_messages_.push_back(RouteMessage());
+ inbound_messages_.back().type = RouteMessage::TEXT;
+ inbound_messages_.back().text = message;
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&FakeMediaRouter::DoDeliverInboundMessages,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void OnBinaryMessageFromProvider(const std::vector<uint8_t>& message) {
+ inbound_messages_.push_back(RouteMessage());
+ inbound_messages_.back().type = RouteMessage::BINARY;
+ inbound_messages_.back().binary = std::vector<uint8_t>(message);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&FakeMediaRouter::DoDeliverInboundMessages,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void TakeMessagesSentToProvider(RouteMessage::Type type,
+ std::vector<RouteMessage>* messages) {
+ decltype(outbound_messages_) untaken_messages;
+ for (const auto& entry : outbound_messages_) {
+ if (entry.first.type == type) {
+ messages->push_back(entry.first);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(entry.second, true));
+ } else {
+ untaken_messages.push_back(entry);
+ }
+ }
+ outbound_messages_.swap(untaken_messages);
+ }
+
+ protected:
+ void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) final {
+ CHECK(!routes_observer_);
+ routes_observer_ = observer;
+ CHECK(routes_observer_);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&FakeMediaRouter::DoUpdateRoutes,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) final {
+ CHECK_EQ(routes_observer_, observer);
+ routes_observer_ = nullptr;
+ }
+
+ void RegisterRouteMessageObserver(RouteMessageObserver* observer) final {
+ CHECK(!message_observer_);
+ message_observer_ = observer;
+ CHECK(message_observer_);
+ }
+
+ void UnregisterRouteMessageObserver(RouteMessageObserver* observer) final {
+ CHECK_EQ(message_observer_, observer);
+ message_observer_ = nullptr;
+ }
+
+ void SendRouteMessage(const MediaRoute::Id& route_id,
+ const std::string& text,
+ const SendRouteMessageCallback& callback) final {
+ EXPECT_EQ(message_observer_->route_id(), route_id);
+ ASSERT_FALSE(callback.is_null());
+ RouteMessage message;
+ message.type = RouteMessage::TEXT;
+ message.text = text;
+ outbound_messages_.push_back(std::make_pair(message, callback));
+ }
+
+ void SendRouteBinaryMessage(
+ const MediaRoute::Id& route_id,
+ std::unique_ptr<std::vector<uint8_t>> data,
+ const SendRouteMessageCallback& callback) final {
+ EXPECT_EQ(message_observer_->route_id(), route_id);
+ ASSERT_TRUE(!!data);
+ ASSERT_FALSE(callback.is_null());
+ RouteMessage message;
+ message.type = RouteMessage::BINARY;
+ message.binary = std::move(*data);
+ outbound_messages_.push_back(std::make_pair(message, callback));
+ }
+
+ private:
+ // Asynchronous callback to notify the MediaRoutesObserver of a change in
+ // routes.
+ void DoUpdateRoutes() {
+ if (routes_observer_)
+ routes_observer_->OnRoutesUpdated(routes_, std::vector<MediaRoute::Id>());
+ }
+
+ // Asynchronous callback to deliver messages to the RouteMessageObserver.
+ void DoDeliverInboundMessages() {
+ if (message_observer_)
+ message_observer_->OnMessagesReceived(inbound_messages_);
+ inbound_messages_.clear();
+ }
+
+ MediaRoutesObserver* routes_observer_;
+ RouteMessageObserver* message_observer_;
+
+ std::vector<MediaRoute> routes_;
+ // Messages from Cast Provider to the connector.
+ std::vector<RouteMessage> inbound_messages_;
+ // Messages from the connector to the Cast Provider.
+ using OutboundMessageAndCallback =
+ std::pair<RouteMessage, SendRouteMessageCallback>;
+ std::vector<OutboundMessageAndCallback> outbound_messages_;
+
+ base::WeakPtrFactory<FakeMediaRouter> weak_factory_;
+};
+
+class MockRemotingSource : public media::mojom::RemotingSource {
+ public:
+ MockRemotingSource() : binding_(this) {}
+ ~MockRemotingSource() final {}
+
+ void Bind(RemotingSourceRequest request) {
+ binding_.Bind(std::move(request));
+ }
+
+ MOCK_METHOD0(OnSinkAvailable, void());
+ MOCK_METHOD0(OnSinkGone, void());
+ MOCK_METHOD0(OnStarted, void());
+ MOCK_METHOD1(OnStartFailed, void(RemotingStartFailReason));
+ MOCK_METHOD1(OnMessageFromSink, void(const std::vector<uint8_t>&));
+ MOCK_METHOD1(OnStopped, void(RemotingStopReason));
+
+ private:
+ mojo::Binding<media::mojom::RemotingSource> binding_;
+};
+
+} // namespace
+
+class CastRemotingConnectorTest : public ::testing::Test {
+ public:
+ CastRemotingConnectorTest()
+ : remoting_source_(kRemotingMediaSource),
+ connector_(&media_router_, remoting_source_.id()) {}
+
+ void TearDown() final {
+ // Allow any pending Mojo operations to complete before destruction. For
+ // example, when one end of a Mojo message pipe is closed, a task is posted
+ // to later destroy objects that were owned by the message pipe.
+ RunUntilIdle();
+ }
+
+ protected:
+ RemoterPtr CreateRemoter(MockRemotingSource* source) {
+ RemotingSourcePtr source_ptr;
+ source->Bind(mojo::GetProxy(&source_ptr));
+ RemoterPtr remoter_ptr;
+ connector_.CreateBridge(std::move(source_ptr),
+ mojo::GetProxy(&remoter_ptr));
+ return remoter_ptr;
+ }
+
+ void ProviderDiscoversSink() {
+ media_router_.OnRemotingRouteExists(true);
+ }
+
+ void ProviderLosesSink() {
+ media_router_.OnRemotingRouteExists(false);
+ }
+
+ void ConnectorSentMessageToProvider(const std::string& expected_message) {
+ std::vector<RouteMessage> messages;
+ media_router_.TakeMessagesSentToProvider(RouteMessage::TEXT, &messages);
+ bool did_see_expected_message = false;
+ for (const RouteMessage& message : messages) {
+ if (message.text && expected_message == *message.text) {
+ did_see_expected_message = true;
+ } else {
+ ADD_FAILURE()
+ << "Unexpected message: " << message.ToHumanReadableString();
+ }
+ }
+ EXPECT_TRUE(did_see_expected_message);
+ }
+
+ void ConnectorSentMessageToSink(
+ const std::vector<uint8_t>& expected_message) {
+ std::vector<RouteMessage> messages;
+ media_router_.TakeMessagesSentToProvider(RouteMessage::BINARY, &messages);
+ bool did_see_expected_message = false;
+ for (const RouteMessage& message : messages) {
+ if (message.binary && expected_message == *message.binary) {
+ did_see_expected_message = true;
+ } else {
+ ADD_FAILURE()
+ << "Unexpected message: " << message.ToHumanReadableString();
+ }
+ }
+ EXPECT_TRUE(did_see_expected_message);
+ }
+
+ void ConnectorSentNoMessagesToProvider() {
+ std::vector<RouteMessage> messages;
+ media_router_.TakeMessagesSentToProvider(RouteMessage::TEXT, &messages);
+ EXPECT_TRUE(messages.empty());
+ }
+
+ void ConnectorSentNoMessagesToSink() {
+ std::vector<RouteMessage> messages;
+ media_router_.TakeMessagesSentToProvider(RouteMessage::BINARY, &messages);
+ EXPECT_TRUE(messages.empty());
+ }
+
+ void ProviderPassesMessageFromSink(
+ const std::vector<uint8_t>& message) {
+ media_router_.OnBinaryMessageFromProvider(message);
+ }
+
+ void ProviderSaysToRemotingConnector(const std::string& message) {
+ media_router_.OnMessageFromProvider(message);
+ }
+
+ void MediaRouterTerminatesRoute() {
+ media_router_.OnRemotingRouteExists(false);
+ }
+
+ static void RunUntilIdle() {
+ base::RunLoop().RunUntilIdle();
+ }
+
+ private:
+ content::TestBrowserThreadBundle browser_thread_bundle_;
+ FakeMediaRouter media_router_;
+ const MediaSource remoting_source_;
+ CastRemotingConnector connector_;
+};
+
+TEST_F(CastRemotingConnectorTest, NeverNotifiesThatSinkIsAvailable) {
+ MockRemotingSource source;
+ RemoterPtr remoter = CreateRemoter(&source);
+
+ EXPECT_CALL(source, OnSinkAvailable()).Times(0);
+ EXPECT_CALL(source, OnSinkGone()).Times(AtLeast(0));
+ RunUntilIdle();
+}
+
+TEST_F(CastRemotingConnectorTest, NotifiesWhenSinkIsAvailableAndThenGone) {
+ MockRemotingSource source;
+ RemoterPtr remoter = CreateRemoter(&source);
+
+ EXPECT_CALL(source, OnSinkAvailable()).Times(1);
+ ProviderDiscoversSink();
+ RunUntilIdle();
+
+ EXPECT_CALL(source, OnSinkGone()).Times(AtLeast(1));
+ ProviderLosesSink();
+ RunUntilIdle();
+}
+
+TEST_F(CastRemotingConnectorTest,
+ NotifiesMultipleSourcesWhenSinkIsAvailableAndThenGone) {
+ MockRemotingSource source1;
+ RemoterPtr remoter1 = CreateRemoter(&source1);
+ MockRemotingSource source2;
+ RemoterPtr remoter2 = CreateRemoter(&source2);
+
+ EXPECT_CALL(source1, OnSinkAvailable()).Times(1);
+ EXPECT_CALL(source2, OnSinkAvailable()).Times(1);
+ ProviderDiscoversSink();
+ RunUntilIdle();
+
+ EXPECT_CALL(source1, OnSinkGone()).Times(AtLeast(1));
+ EXPECT_CALL(source2, OnSinkGone()).Times(AtLeast(1));
+ ProviderLosesSink();
+ RunUntilIdle();
+}
+
+TEST_F(CastRemotingConnectorTest, HandlesTeardownOfRemotingSourceFirst) {
+ std::unique_ptr<MockRemotingSource> source(new MockRemotingSource);
+ RemoterPtr remoter = CreateRemoter(source.get());
+
+ EXPECT_CALL(*source, OnSinkAvailable()).Times(1);
+ ProviderDiscoversSink();
+ RunUntilIdle();
+
+ source.reset();
+ RunUntilIdle();
+}
+
+TEST_F(CastRemotingConnectorTest, HandlesTeardownOfRemoterFirst) {
+ MockRemotingSource source;
+ RemoterPtr remoter = CreateRemoter(&source);
+
+ EXPECT_CALL(source, OnSinkAvailable()).Times(1);
+ ProviderDiscoversSink();
+ RunUntilIdle();
+
+ remoter.reset();
+ RunUntilIdle();
+}
+
+namespace {
+
+// The possible ways a remoting session may be terminated in the "full
+// run-through" tests.
+enum HowItEnds {
+ SOURCE_TERMINATES, // The render process decides to end remoting.
+ MOJO_PIPE_CLOSES, // A Mojo message pipe closes unexpectedly.
+ ROUTE_TERMINATES, // The Media Router UI was used to terminate the route.
+ EXTERNAL_FAILURE, // The sink is cut-off, perhaps due to a network outage.
+};
+
+} // namespace
+
+class CastRemotingConnectorFullSessionTest
+ : public CastRemotingConnectorTest,
+ public ::testing::WithParamInterface<HowItEnds> {
+ public:
+ HowItEnds how_it_ends() const { return GetParam(); }
+};
+
+// Performs a full run-through of starting and stopping remoting, with
+// communications between source and sink established at the correct times, and
+// tests that end-to-end behavior is correct depending on what caused the
+// remoting session to end.
+TEST_P(CastRemotingConnectorFullSessionTest, GoesThroughAllTheMotions) {
+ std::unique_ptr<MockRemotingSource> source(new MockRemotingSource());
+ RemoterPtr remoter = CreateRemoter(source.get());
+ std::unique_ptr<MockRemotingSource> other_source(new MockRemotingSource());
+ RemoterPtr other_remoter = CreateRemoter(other_source.get());
+
+ // Throughout this test |other_source| should not participate in the
+ // remoting session, and so these method calls should never occur:
+ EXPECT_CALL(*other_source, OnStarted()).Times(0);
+ EXPECT_CALL(*other_source, OnStopped(_)).Times(0);
+ EXPECT_CALL(*other_source, OnMessageFromSink(_)).Times(0);
+
+ // Both sinks should be notified when the Cast Provider tells the connector
+ // a remoting sink is available.
+ EXPECT_CALL(*source, OnSinkAvailable()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*other_source, OnSinkAvailable()).Times(1).RetiresOnSaturation();
+ ProviderDiscoversSink();
+ RunUntilIdle();
+
+ // When |source| starts a remoting session, |other_source| is notified the
+ // sink is gone, the Cast Provider is notified that remoting has started,
+ // and |source| is notified that its request was successful.
+ EXPECT_CALL(*source, OnStarted()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*other_source, OnSinkGone()).Times(1).RetiresOnSaturation();
+ remoter->Start();
+ RunUntilIdle();
+ ConnectorSentMessageToProvider("START_CAST_REMOTING:session=1");
+
+ // The |source| should now be able to send binary messages to the sink.
+ // |other_source| should not!
+ const std::vector<uint8_t> message_to_sink = { 3, 1, 4, 1, 5, 9 };
+ remoter->SendMessageToSink(message_to_sink);
+ const std::vector<uint8_t> ignored_message_to_sink = { 1, 2, 3 };
+ other_remoter->SendMessageToSink(ignored_message_to_sink);
+ RunUntilIdle();
+ ConnectorSentMessageToSink(message_to_sink);
+
+ // The sink should also be able to send binary messages to the |source|.
+ const std::vector<uint8_t> message_to_source = { 2, 7, 1, 8, 2, 8 };
+ EXPECT_CALL(*source, OnMessageFromSink(message_to_source)).Times(1)
+ .RetiresOnSaturation();
+ ProviderPassesMessageFromSink(message_to_source);
+ RunUntilIdle();
+
+ // The |other_source| should not be allowed to start a remoting session.
+ EXPECT_CALL(*other_source,
+ OnStartFailed(RemotingStartFailReason::CANNOT_START_MULTIPLE))
+ .Times(1).RetiresOnSaturation();
+ other_remoter->Start();
+ RunUntilIdle();
+ ConnectorSentNoMessagesToProvider();
+
+ // What happens from here depends on how this remoting session will end...
+ switch (how_it_ends()) {
+ case SOURCE_TERMINATES: {
+ // When the |source| stops the remoting session, the Cast Provider is
+ // notified the session has stopped, and the |source| receives both an
+ // OnStopped() and an OnSinkGone() notification.
+ const RemotingStopReason reason = RemotingStopReason::LOCAL_PLAYBACK;
+ EXPECT_CALL(*source, OnSinkGone()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*source, OnStopped(reason)).Times(1).RetiresOnSaturation();
+ remoter->Stop(reason);
+ RunUntilIdle();
+ ConnectorSentMessageToProvider("STOP_CAST_REMOTING:session=1");
+
+ // Since remoting is stopped, any further messaging in either direction
+ // must be dropped.
+ const std::vector<uint8_t> message_to_sink = { 1, 6, 1, 8, 0, 3 };
+ const std::vector<uint8_t> message_to_source = { 6, 2, 8, 3, 1, 8 };
+ EXPECT_CALL(*source, OnMessageFromSink(_)).Times(0);
+ remoter->SendMessageToSink(message_to_sink);
+ ProviderPassesMessageFromSink(message_to_source);
+ RunUntilIdle();
+ ConnectorSentNoMessagesToSink();
+
+ // When the sink is ready, the Cast Provider sends a notification to the
+ // connector. The connector will notify both sources that a sink is once
+ // again available.
+ EXPECT_CALL(*source, OnSinkAvailable()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*other_source, OnSinkAvailable()).Times(1)
+ .RetiresOnSaturation();
+ ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+ RunUntilIdle();
+
+ // When the sink is no longer available, the Cast Provider notifies the
+ // connector, and both sources are then notified the sink is gone.
+ EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1));
+ EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(1));
+ ProviderLosesSink();
+ RunUntilIdle();
+
+ break;
+ }
+
+ case MOJO_PIPE_CLOSES:
+ // When the Mojo pipes for |other_source| close, this should not affect
+ // the current remoting session.
+ other_source.reset();
+ other_remoter.reset();
+ RunUntilIdle();
+ ConnectorSentNoMessagesToProvider();
+
+ // Now, when the Mojo pipes for |source| close, the Cast Provider will be
+ // notified that the session has stopped.
+ source.reset();
+ remoter.reset();
+ RunUntilIdle();
+ ConnectorSentMessageToProvider("STOP_CAST_REMOTING:session=1");
+
+ // The Cast Provider will detect when the sink is ready for the next
+ // remoting session, and then notify the connector. However, there are no
+ // sources to propagate this notification to.
+ ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+ RunUntilIdle();
+
+ break;
+
+ case ROUTE_TERMINATES:
+ // When the Media Router terminates the route (e.g., because a user
+ // terminated the route from the UI), the source and sink are immediately
+ // cut off from one another.
+ EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1));
+ EXPECT_CALL(*source, OnStopped(RemotingStopReason::ROUTE_TERMINATED))
+ .Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(0));
+ MediaRouterTerminatesRoute();
+ RunUntilIdle();
+ ConnectorSentNoMessagesToProvider();
+
+ // Furthermore, the connector and Cast Provider are also cut off from one
+ // another and should not be able to exchange messages anymore. Therefore,
+ // the connector will never try to notify the sources that the sink is
+ // available again.
+ EXPECT_CALL(*source, OnSinkAvailable()).Times(0);
+ EXPECT_CALL(*other_source, OnSinkAvailable()).Times(0);
+ ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+ RunUntilIdle();
+
+ break;
+
+ case EXTERNAL_FAILURE: {
+ // When the Cast Provider is cut-off from the sink, it sends a fail
+ // notification to the connector. The connector, in turn, force-stops the
+ // remoting session and notifies the |source|.
+ EXPECT_CALL(*source, OnSinkGone()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*source, OnStopped(RemotingStopReason::UNEXPECTED_FAILURE))
+ .Times(1).RetiresOnSaturation();
+ ProviderSaysToRemotingConnector("FAILED_CAST_REMOTING:session=1");
+ RunUntilIdle();
+
+ // Since remoting is stopped, any further messaging in either direction
+ // must be dropped.
+ const std::vector<uint8_t> message_to_sink = { 1, 6, 1, 8, 0, 3 };
+ const std::vector<uint8_t> message_to_source = { 6, 2, 8, 3, 1, 8 };
+ EXPECT_CALL(*source, OnMessageFromSink(_)).Times(0);
+ remoter->SendMessageToSink(message_to_sink);
+ ProviderPassesMessageFromSink(message_to_source);
+ RunUntilIdle();
+ ConnectorSentNoMessagesToSink();
+
+ // Later, if whatever caused the external failure has resolved, the Cast
+ // Provider will notify the connector that the sink is available one
+ // again.
+ EXPECT_CALL(*source, OnSinkAvailable()).Times(1).RetiresOnSaturation();
+ EXPECT_CALL(*other_source, OnSinkAvailable()).Times(1)
+ .RetiresOnSaturation();
+ ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+ RunUntilIdle();
+
+ // When the sink is no longer available, the Cast Provider notifies the
+ // connector, and both sources are then notified the sink is gone.
+ EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1));
+ EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(1));
+ ProviderLosesSink();
+ RunUntilIdle();
+
+ break;
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(, CastRemotingConnectorFullSessionTest,
+ ::testing::Values(SOURCE_TERMINATES,
+ MOJO_PIPE_CLOSES,
+ ROUTE_TERMINATES,
+ EXTERNAL_FAILURE));

Powered by Google App Engine
This is Rietveld 408576698