| // 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 <utility> |
| |
| #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/service_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 = "deviceId1"; |
| 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 ServiceInfo& receiver, int32_t socket_id) { |
| platform_client_.AddOrUpdateReceiver(receiver, socket_id); |
| app_discovery_service_.AddOrUpdateReceiver(receiver); |
| } |
| |
| CastAppDiscoveryService::Subscription StartObservingAvailability( |
| const CastMediaSource& source, |
| std::vector<ServiceInfo>* save_receivers) { |
| return app_discovery_service_.StartObservingAvailability( |
| source, [save_receivers](const CastMediaSource& source, |
| const std::vector<ServiceInfo>& receivers) { |
| *save_receivers = receivers; |
| }); |
| } |
| |
| CastAppDiscoveryService::Subscription StartSourceA1Query( |
| std::vector<ServiceInfo>* 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"}}; |
| |
| ServiceInfo receiver_; |
| }; |
| |
| TEST_F(CastAppDiscoveryServiceImplTest, StartObservingAvailability) { |
| std::vector<ServiceInfo> 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<ServiceInfo> 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, "deviceId1"); |
| EXPECT_EQ(receivers2[0].unique_id, "deviceId1"); |
| |
| // 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, "deviceId1"); |
| } |
| |
| TEST_F(CastAppDiscoveryServiceImplTest, ReAddAvailQueryUsesCachedValue) { |
| std::vector<ServiceInfo> 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, "deviceId1"); |
| |
| 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, "deviceId1"); |
| } |
| |
| TEST_F(CastAppDiscoveryServiceImplTest, AvailQueryUpdatedOnReceiverUpdate) { |
| std::vector<ServiceInfo> 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, "deviceId1"); |
| |
| // 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<ServiceInfo> receivers1; |
| auto subscription1 = StartObservingAvailability(source_a_1_, &receivers1); |
| std::vector<ServiceInfo> 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, "deviceId1"); |
| |
| // 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<ServiceInfo> 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<ServiceInfo> 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)); |
| ServiceInfo receiver2; |
| receiver2.unique_id = "deviceId2"; |
| 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<ServiceInfo> 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, "deviceId1"); |
| |
| // Same app ID should not trigger another request, but it should return |
| // cached value. |
| EXPECT_CALL(peer_client(), OnMessage(_, _)).Times(0); |
| std::vector<ServiceInfo> receivers2; |
| auto subscription2 = StartObservingAvailability(source_a_2_, &receivers2); |
| ASSERT_EQ(receivers2.size(), 1u); |
| EXPECT_EQ(receivers2[0].unique_id, "deviceId1"); |
| } |
| |
| TEST_F(CastAppDiscoveryServiceImplTest, AvailabilityUnknownOrUnavailable) { |
| std::vector<ServiceInfo> 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 |