// Copyright 2020 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 "cast/sender/cast_app_discovery_service_impl.h" #include #include "cast/common/channel/testing/fake_cast_socket.h" #include "cast/common/channel/testing/mock_socket_error_handler.h" #include "cast/common/channel/virtual_connection_router.h" #include "cast/common/public/receiver_info.h" #include "cast/sender/testing/test_helpers.h" #include "gtest/gtest.h" #include "platform/test/fake_clock.h" #include "platform/test/fake_task_runner.h" #include "util/osp_logging.h" namespace openscreen { namespace cast { using ::cast::channel::CastMessage; using ::testing::_; class CastAppDiscoveryServiceImplTest : public ::testing::Test { public: void SetUp() override { socket_id_ = fake_cast_socket_pair_.socket->socket_id(); router_.TakeSocket(&mock_error_handler_, std::move(fake_cast_socket_pair_.socket)); receiver_.v4_address = fake_cast_socket_pair_.remote_endpoint.address; receiver_.port = fake_cast_socket_pair_.remote_endpoint.port; receiver_.unique_id = "receiverId1"; receiver_.friendly_name = "Some Name"; } protected: CastSocket& peer_socket() { return *fake_cast_socket_pair_.peer_socket; } MockCastSocketClient& peer_client() { return fake_cast_socket_pair_.mock_peer_client; } void AddOrUpdateReceiver(const ReceiverInfo& receiver, int32_t socket_id) { platform_client_.AddOrUpdateReceiver(receiver, socket_id); app_discovery_service_.AddOrUpdateReceiver(receiver); } CastAppDiscoveryService::Subscription StartObservingAvailability( const CastMediaSource& source, std::vector* save_receivers) { return app_discovery_service_.StartObservingAvailability( source, [save_receivers](const CastMediaSource& source, const std::vector& receivers) { *save_receivers = receivers; }); } CastAppDiscoveryService::Subscription StartSourceA1Query( std::vector* receivers, int* request_id, std::string* sender_id) { auto subscription = StartObservingAvailability(source_a_1_, receivers); // Adding a receiver after app registered causes app availability request to // be sent. *request_id = -1; *sender_id = ""; EXPECT_CALL(peer_client(), OnMessage(_, _)) .WillOnce([request_id, sender_id](CastSocket*, CastMessage message) { VerifyAppAvailabilityRequest(message, "AAA", request_id, sender_id); }); AddOrUpdateReceiver(receiver_, socket_id_); return subscription; } FakeCastSocketPair fake_cast_socket_pair_; int32_t socket_id_; MockSocketErrorHandler mock_error_handler_; VirtualConnectionRouter router_; FakeClock clock_{Clock::now()}; FakeTaskRunner task_runner_{&clock_}; CastPlatformClient platform_client_{&router_, &FakeClock::now, &task_runner_}; CastAppDiscoveryServiceImpl app_discovery_service_{&platform_client_, &FakeClock::now}; CastMediaSource source_a_1_{"cast:AAA?clientId=1", {"AAA"}}; CastMediaSource source_a_2_{"cast:AAA?clientId=2", {"AAA"}}; CastMediaSource source_b_1_{"cast:BBB?clientId=1", {"BBB"}}; ReceiverInfo receiver_; }; TEST_F(CastAppDiscoveryServiceImplTest, StartObservingAvailability) { std::vector receivers1; int request_id; std::string sender_id; auto subscription1 = StartSourceA1Query(&receivers1, &request_id, &sender_id); // Same app ID should not trigger another request. EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); std::vector receivers2; auto subscription2 = StartObservingAvailability(source_a_2_, &receivers2); CastMessage availability_response = CreateAppAvailableResponseChecked(request_id, sender_id, "AAA"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); ASSERT_EQ(receivers1.size(), 1u); ASSERT_EQ(receivers2.size(), 1u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); EXPECT_EQ(receivers2[0].unique_id, "receiverId1"); // No more updates for |source_a_1_| (i.e. |receivers1|). subscription1.Reset(); platform_client_.RemoveReceiver(receiver_); app_discovery_service_.RemoveReceiver(receiver_); ASSERT_EQ(receivers1.size(), 1u); EXPECT_EQ(receivers2.size(), 0u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); } TEST_F(CastAppDiscoveryServiceImplTest, ReAddAvailQueryUsesCachedValue) { std::vector receivers1; int request_id; std::string sender_id; auto subscription1 = StartSourceA1Query(&receivers1, &request_id, &sender_id); CastMessage availability_response = CreateAppAvailableResponseChecked(request_id, sender_id, "AAA"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); ASSERT_EQ(receivers1.size(), 1u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); subscription1.Reset(); receivers1.clear(); // Request not re-sent; cached kAvailable value is used. EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); subscription1 = StartObservingAvailability(source_a_1_, &receivers1); ASSERT_EQ(receivers1.size(), 1u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); } TEST_F(CastAppDiscoveryServiceImplTest, AvailQueryUpdatedOnReceiverUpdate) { std::vector receivers1; int request_id; std::string sender_id; auto subscription1 = StartSourceA1Query(&receivers1, &request_id, &sender_id); // Result set now includes |receiver_|. CastMessage availability_response = CreateAppAvailableResponseChecked(request_id, sender_id, "AAA"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); ASSERT_EQ(receivers1.size(), 1u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); // Updating |receiver_| causes |source_a_1_| query to be updated, but it's too // soon for a new message to be sent. EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); receiver_.friendly_name = "New Name"; AddOrUpdateReceiver(receiver_, socket_id_); ASSERT_EQ(receivers1.size(), 1u); EXPECT_EQ(receivers1[0].friendly_name, "New Name"); } TEST_F(CastAppDiscoveryServiceImplTest, Refresh) { std::vector receivers1; auto subscription1 = StartObservingAvailability(source_a_1_, &receivers1); std::vector receivers2; auto subscription2 = StartObservingAvailability(source_b_1_, &receivers2); // Adding a receiver after app registered causes two separate app availability // requests to be sent. int request_idA = -1; int request_idB = -1; std::string sender_id = ""; EXPECT_CALL(peer_client(), OnMessage(_, _)) .Times(2) .WillRepeatedly([&request_idA, &request_idB, &sender_id]( CastSocket*, CastMessage message) { std::string app_id; int request_id = -1; VerifyAppAvailabilityRequest(message, &app_id, &request_id, &sender_id); if (app_id == "AAA") { EXPECT_EQ(request_idA, -1); request_idA = request_id; } else if (app_id == "BBB") { EXPECT_EQ(request_idB, -1); request_idB = request_id; } else { EXPECT_TRUE(false); } }); AddOrUpdateReceiver(receiver_, socket_id_); CastMessage availability_response = CreateAppAvailableResponseChecked(request_idA, sender_id, "AAA"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); availability_response = CreateAppUnavailableResponseChecked(request_idB, sender_id, "BBB"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); ASSERT_EQ(receivers1.size(), 1u); ASSERT_EQ(receivers2.size(), 0u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); // Not enough time has passed for a refresh. clock_.Advance(std::chrono::seconds(30)); EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); app_discovery_service_.Refresh(); // Refresh will now query again for unavailable app IDs. clock_.Advance(std::chrono::minutes(2)); EXPECT_CALL(peer_client(), OnMessage(_, _)) .WillOnce([&request_idB, &sender_id](CastSocket*, CastMessage message) { VerifyAppAvailabilityRequest(message, "BBB", &request_idB, &sender_id); }); app_discovery_service_.Refresh(); } TEST_F(CastAppDiscoveryServiceImplTest, StartObservingAvailabilityAfterReceiverAdded) { // No registered apps. EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); AddOrUpdateReceiver(receiver_, socket_id_); // Registering apps immediately sends requests to |receiver_|. int request_idA = -1; std::string sender_id = ""; EXPECT_CALL(peer_client(), OnMessage(_, _)) .WillOnce([&request_idA, &sender_id](CastSocket*, CastMessage message) { VerifyAppAvailabilityRequest(message, "AAA", &request_idA, &sender_id); }); std::vector receivers1; auto subscription1 = StartObservingAvailability(source_a_1_, &receivers1); int request_idB = -1; EXPECT_CALL(peer_client(), OnMessage(_, _)) .WillOnce([&request_idB, &sender_id](CastSocket*, CastMessage message) { VerifyAppAvailabilityRequest(message, "BBB", &request_idB, &sender_id); }); std::vector receivers2; auto subscription2 = StartObservingAvailability(source_b_1_, &receivers2); // Add a new receiver with a corresponding socket. FakeCastSocketPair fake_sockets2({{192, 168, 1, 17}, 2345}, {{192, 168, 1, 19}, 2345}); CastSocket* socket2 = fake_sockets2.socket.get(); router_.TakeSocket(&mock_error_handler_, std::move(fake_sockets2.socket)); ReceiverInfo receiver2; receiver2.unique_id = "receiverId2"; receiver2.v4_address = fake_sockets2.remote_endpoint.address; receiver2.port = fake_sockets2.remote_endpoint.port; // Adding new receiver causes availability requests for both apps to be sent // to the new receiver. request_idA = -1; request_idB = -1; EXPECT_CALL(fake_sockets2.mock_peer_client, OnMessage(_, _)) .Times(2) .WillRepeatedly([&request_idA, &request_idB, &sender_id]( CastSocket*, CastMessage message) { std::string app_id; int request_id = -1; VerifyAppAvailabilityRequest(message, &app_id, &request_id, &sender_id); if (app_id == "AAA") { EXPECT_EQ(request_idA, -1); request_idA = request_id; } else if (app_id == "BBB") { EXPECT_EQ(request_idB, -1); request_idB = request_id; } else { EXPECT_TRUE(false); } }); AddOrUpdateReceiver(receiver2, socket2->socket_id()); } TEST_F(CastAppDiscoveryServiceImplTest, StartObservingAvailabilityCachedValue) { std::vector receivers1; int request_id; std::string sender_id; auto subscription1 = StartSourceA1Query(&receivers1, &request_id, &sender_id); CastMessage availability_response = CreateAppAvailableResponseChecked(request_id, sender_id, "AAA"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); ASSERT_EQ(receivers1.size(), 1u); EXPECT_EQ(receivers1[0].unique_id, "receiverId1"); // Same app ID should not trigger another request, but it should return // cached value. EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); std::vector receivers2; auto subscription2 = StartObservingAvailability(source_a_2_, &receivers2); ASSERT_EQ(receivers2.size(), 1u); EXPECT_EQ(receivers2[0].unique_id, "receiverId1"); } TEST_F(CastAppDiscoveryServiceImplTest, AvailabilityUnknownOrUnavailable) { std::vector receivers1; int request_id; std::string sender_id; auto subscription1 = StartSourceA1Query(&receivers1, &request_id, &sender_id); // The request will timeout resulting in unknown app availability. clock_.Advance(std::chrono::seconds(10)); task_runner_.RunTasksUntilIdle(); EXPECT_EQ(receivers1.size(), 0u); // Receiver updated together with unknown app availability will cause a // request to be sent again. request_id = -1; EXPECT_CALL(peer_client(), OnMessage(_, _)) .WillOnce([&request_id, &sender_id](CastSocket*, CastMessage message) { VerifyAppAvailabilityRequest(message, "AAA", &request_id, &sender_id); }); AddOrUpdateReceiver(receiver_, socket_id_); CastMessage availability_response = CreateAppUnavailableResponseChecked(request_id, sender_id, "AAA"); EXPECT_TRUE(peer_socket().Send(availability_response).ok()); // Known availability so no request sent. EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); AddOrUpdateReceiver(receiver_, socket_id_); // Removing the receiver will also remove previous availability information. // Next time the receiver is added, a new request will be sent. platform_client_.RemoveReceiver(receiver_); app_discovery_service_.RemoveReceiver(receiver_); request_id = -1; EXPECT_CALL(peer_client(), OnMessage(_, _)) .WillOnce([&request_id, &sender_id](CastSocket*, CastMessage message) { VerifyAppAvailabilityRequest(message, "AAA", &request_id, &sender_id); }); AddOrUpdateReceiver(receiver_, socket_id_); } } // namespace cast } // namespace openscreen