// Copyright 2019 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 "osp/public/presentation/presentation_controller.h" #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "osp/impl/presentation/testing/mock_connection_delegate.h" #include "osp/impl/quic/testing/quic_test_support.h" #include "osp/impl/service_listener_impl.h" #include "osp/public/message_demuxer.h" #include "osp/public/network_service_manager.h" #include "osp/public/testing/message_demuxer_test_support.h" #include "platform/test/fake_clock.h" #include "platform/test/fake_task_runner.h" namespace openscreen { namespace osp { using ::testing::_; using ::testing::Invoke; using ::testing::NiceMock; namespace { const char kTestUrl[] = "https://example.foo"; class MockServiceListenerDelegate final : public ServiceListenerImpl::Delegate { public: ~MockServiceListenerDelegate() override = default; ServiceListenerImpl* listener() { return listener_; } MOCK_METHOD0(StartListener, void()); MOCK_METHOD0(StartAndSuspendListener, void()); MOCK_METHOD0(StopListener, void()); MOCK_METHOD0(SuspendListener, void()); MOCK_METHOD0(ResumeListener, void()); MOCK_METHOD1(SearchNow, void(ServiceListener::State from)); MOCK_METHOD0(RunTasksListener, void()); }; class MockReceiverObserver final : public ReceiverObserver { public: ~MockReceiverObserver() override = default; MOCK_METHOD2(OnRequestFailed, void(const std::string& presentation_url, const std::string& service_id)); MOCK_METHOD2(OnReceiverAvailable, void(const std::string& presentation_url, const std::string& service_id)); MOCK_METHOD2(OnReceiverUnavailable, void(const std::string& presentation_url, const std::string& service_id)); }; class MockRequestDelegate final : public RequestDelegate { public: MockRequestDelegate() = default; ~MockRequestDelegate() override = default; void OnConnection(std::unique_ptr connection) override { OnConnectionMock(connection); } MOCK_METHOD1(OnConnectionMock, void(std::unique_ptr& connection)); MOCK_METHOD1(OnError, void(const Error& error)); }; } // namespace class ControllerTest : public ::testing::Test { public: ControllerTest() { fake_clock_ = std::make_unique( Clock::time_point(std::chrono::seconds(11111))); task_runner_ = std::make_unique(fake_clock_.get()); quic_bridge_ = std::make_unique(task_runner_.get(), FakeClock::now); receiver_info1 = { "service-id1", "lucas-auer", 1, quic_bridge_->kReceiverEndpoint, {}}; } protected: void SetUp() override { auto service_listener = std::make_unique(&mock_listener_delegate_); NetworkServiceManager::Create(std::move(service_listener), nullptr, std::move(quic_bridge_->quic_client), std::move(quic_bridge_->quic_server)); controller_ = std::make_unique(FakeClock::now); ON_CALL(quic_bridge_->mock_server_observer, OnIncomingConnectionMock(_)) .WillByDefault( Invoke([this](std::unique_ptr& connection) { controller_endpoint_id_ = connection->endpoint_id(); })); availability_watch_ = quic_bridge_->receiver_demuxer->SetDefaultMessageTypeWatch( msgs::Type::kPresentationUrlAvailabilityRequest, &mock_callback_); } void TearDown() override { availability_watch_ = MessageDemuxer::MessageWatch(); controller_.reset(); NetworkServiceManager::Dispose(); } void ExpectAvailabilityRequest( msgs::PresentationUrlAvailabilityRequest* request) { ssize_t decode_result = -1; msgs::Type msg_type; EXPECT_CALL(mock_callback_, OnStreamMessage(_, _, _, _, _, _)) .WillOnce(Invoke([request, &msg_type, &decode_result]( uint64_t endpoint_id, uint64_t cid, msgs::Type message_type, const uint8_t* buffer, size_t buffer_size, Clock::time_point now) { msg_type = message_type; decode_result = msgs::DecodePresentationUrlAvailabilityRequest( buffer, buffer_size, request); return decode_result; })); quic_bridge_->RunTasksUntilIdle(); ASSERT_EQ(msg_type, msgs::Type::kPresentationUrlAvailabilityRequest); ASSERT_GT(decode_result, 0); } void SendAvailabilityResponse( const msgs::PresentationUrlAvailabilityResponse& response) { std::unique_ptr controller_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(controller_connection); ASSERT_EQ(Error::Code::kNone, controller_connection ->WriteMessage( response, msgs::EncodePresentationUrlAvailabilityResponse) .code()); } void SendStartResponse(const msgs::PresentationStartResponse& response) { std::unique_ptr protocol_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(protocol_connection); ASSERT_EQ( Error::Code::kNone, protocol_connection ->WriteMessage(response, msgs::EncodePresentationStartResponse) .code()); } void SendAvailabilityEvent( const msgs::PresentationUrlAvailabilityEvent& event) { std::unique_ptr controller_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(controller_connection); ASSERT_EQ( Error::Code::kNone, controller_connection ->WriteMessage(event, msgs::EncodePresentationUrlAvailabilityEvent) .code()); } void SendTerminationResponse( const msgs::PresentationTerminationResponse& response) { std::unique_ptr protocol_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(protocol_connection); ASSERT_EQ(Error::Code::kNone, protocol_connection ->WriteMessage(response, msgs::EncodePresentationTerminationResponse) .code()); } void SendTerminationEvent(const msgs::PresentationTerminationEvent& event) { std::unique_ptr protocol_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(protocol_connection); ASSERT_EQ( Error::Code::kNone, protocol_connection ->WriteMessage(event, msgs::EncodePresentationTerminationEvent) .code()); } void ExpectCloseRequest(MockMessageCallback* mock_callback, msgs::PresentationConnectionCloseRequest* request, Connection* connection) { ssize_t decode_result = -1; msgs::Type msg_type; EXPECT_CALL(*mock_callback, OnStreamMessage(_, _, _, _, _, _)) .WillOnce(Invoke([request, &msg_type, &decode_result]( uint64_t endpoint_id, uint64_t cid, msgs::Type message_type, const uint8_t* buffer, size_t buffer_size, Clock::time_point now) { msg_type = message_type; decode_result = msgs::DecodePresentationConnectionCloseRequest( buffer, buffer_size, request); return decode_result; })); connection->Close(Connection::CloseReason::kClosed); EXPECT_EQ(connection->state(), Connection::State::kClosed); quic_bridge_->RunTasksUntilIdle(); ASSERT_EQ(msg_type, msgs::Type::kPresentationConnectionCloseRequest); ASSERT_GT(decode_result, 0); } void SendCloseResponse( const msgs::PresentationConnectionCloseResponse& response) { std::unique_ptr protocol_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(protocol_connection); ASSERT_EQ(Error::Code::kNone, protocol_connection ->WriteMessage( response, msgs::EncodePresentationConnectionCloseResponse) .code()); } void SendOpenResponse( const msgs::PresentationConnectionOpenResponse& response) { std::unique_ptr protocol_connection = NetworkServiceManager::Get() ->GetProtocolConnectionServer() ->CreateProtocolConnection(controller_endpoint_id_); ASSERT_TRUE(protocol_connection); ASSERT_EQ(Error::Code::kNone, protocol_connection ->WriteMessage(response, msgs::EncodePresentationConnectionOpenResponse) .code()); } void StartPresentation(MockMessageCallback* mock_callback, MockConnectionDelegate* mock_connection_delegate, std::unique_ptr* connection) { MessageDemuxer::MessageWatch start_presentation_watch = quic_bridge_->receiver_demuxer->SetDefaultMessageTypeWatch( msgs::Type::kPresentationStartRequest, mock_callback); mock_listener_delegate_.listener()->OnReceiverAdded(receiver_info1); quic_bridge_->RunTasksUntilIdle(); MockRequestDelegate mock_request_delegate; msgs::PresentationStartRequest request; msgs::Type msg_type; EXPECT_CALL(*mock_callback, OnStreamMessage(_, _, _, _, _, _)) .WillOnce(Invoke([&request, &msg_type]( uint64_t endpoint_id, uint64_t cid, msgs::Type message_type, const uint8_t* buffer, size_t buffer_size, Clock::time_point now) { msg_type = message_type; ssize_t result = msgs::DecodePresentationStartRequest( buffer, buffer_size, &request); return result; })); Controller::ConnectRequest connect_request = controller_->StartPresentation( "https://example.com/receiver.html", receiver_info1.service_id, &mock_request_delegate, mock_connection_delegate); ASSERT_TRUE(connect_request); quic_bridge_->RunTasksUntilIdle(); ASSERT_EQ(msgs::Type::kPresentationStartRequest, msg_type); msgs::PresentationStartResponse response; response.request_id = request.request_id; response.result = msgs::PresentationStartResponse_result::kSuccess; response.connection_id = 1; SendStartResponse(response); EXPECT_CALL(mock_request_delegate, OnConnectionMock(_)) .WillOnce(Invoke([connection](std::unique_ptr& c) { *connection = std::move(c); })); EXPECT_CALL(*mock_connection_delegate, OnConnected()); quic_bridge_->RunTasksUntilIdle(); ASSERT_TRUE(*connection); } std::unique_ptr fake_clock_; std::unique_ptr task_runner_; MessageDemuxer::MessageWatch availability_watch_; MockMessageCallback mock_callback_; std::unique_ptr quic_bridge_; MockServiceListenerDelegate mock_listener_delegate_; std::unique_ptr controller_; ServiceInfo receiver_info1; MockReceiverObserver mock_receiver_observer_; uint64_t controller_endpoint_id_{0}; }; TEST_F(ControllerTest, ReceiverWatchMoves) { std::vector urls{"one fish", "two fish", "red fish", "gnu fish"}; MockReceiverObserver mock_observer; Controller::ReceiverWatch watch1(controller_.get(), urls, &mock_observer); EXPECT_TRUE(watch1); Controller::ReceiverWatch watch2; EXPECT_FALSE(watch2); watch2 = std::move(watch1); EXPECT_FALSE(watch1); EXPECT_TRUE(watch2); Controller::ReceiverWatch watch3(std::move(watch2)); EXPECT_FALSE(watch2); EXPECT_TRUE(watch3); } TEST_F(ControllerTest, ConnectRequestMoves) { std::string service_id{"service-id1"}; uint64_t request_id = 7; Controller::ConnectRequest request1(controller_.get(), service_id, false, request_id); EXPECT_TRUE(request1); Controller::ConnectRequest request2; EXPECT_FALSE(request2); request2 = std::move(request1); EXPECT_FALSE(request1); EXPECT_TRUE(request2); Controller::ConnectRequest request3(std::move(request2)); EXPECT_FALSE(request2); EXPECT_TRUE(request3); } TEST_F(ControllerTest, ReceiverAvailable) { mock_listener_delegate_.listener()->OnReceiverAdded(receiver_info1); Controller::ReceiverWatch watch = controller_->RegisterReceiverWatch({kTestUrl}, &mock_receiver_observer_); msgs::PresentationUrlAvailabilityRequest request; ExpectAvailabilityRequest(&request); msgs::PresentationUrlAvailabilityResponse response; response.request_id = request.request_id; response.url_availabilities.push_back(msgs::UrlAvailability::kAvailable); SendAvailabilityResponse(response); EXPECT_CALL(mock_receiver_observer_, OnReceiverAvailable(_, _)); quic_bridge_->RunTasksUntilIdle(); MockReceiverObserver mock_receiver_observer2; EXPECT_CALL(mock_receiver_observer2, OnReceiverAvailable(_, _)); Controller::ReceiverWatch watch2 = controller_->RegisterReceiverWatch({kTestUrl}, &mock_receiver_observer2); } TEST_F(ControllerTest, ReceiverWatchCancel) { mock_listener_delegate_.listener()->OnReceiverAdded(receiver_info1); Controller::ReceiverWatch watch = controller_->RegisterReceiverWatch({kTestUrl}, &mock_receiver_observer_); msgs::PresentationUrlAvailabilityRequest request; ExpectAvailabilityRequest(&request); msgs::PresentationUrlAvailabilityResponse response; response.request_id = request.request_id; response.url_availabilities.push_back(msgs::UrlAvailability::kAvailable); SendAvailabilityResponse(response); EXPECT_CALL(mock_receiver_observer_, OnReceiverAvailable(_, _)); quic_bridge_->RunTasksUntilIdle(); MockReceiverObserver mock_receiver_observer2; EXPECT_CALL(mock_receiver_observer2, OnReceiverAvailable(_, _)); Controller::ReceiverWatch watch2 = controller_->RegisterReceiverWatch({kTestUrl}, &mock_receiver_observer2); watch = Controller::ReceiverWatch(); msgs::PresentationUrlAvailabilityEvent event; event.watch_id = request.watch_id; event.url_availabilities.push_back(msgs::UrlAvailability::kUnavailable); EXPECT_CALL(mock_receiver_observer2, OnReceiverUnavailable(_, _)); EXPECT_CALL(mock_receiver_observer_, OnReceiverUnavailable(_, _)).Times(0); SendAvailabilityEvent(event); quic_bridge_->RunTasksUntilIdle(); } TEST_F(ControllerTest, StartPresentation) { MockMessageCallback mock_callback; NiceMock mock_connection_delegate; std::unique_ptr connection; StartPresentation(&mock_callback, &mock_connection_delegate, &connection); } TEST_F(ControllerTest, TerminatePresentationFromController) { MockMessageCallback mock_callback; MockConnectionDelegate mock_connection_delegate; std::unique_ptr connection; StartPresentation(&mock_callback, &mock_connection_delegate, &connection); MessageDemuxer::MessageWatch terminate_presentation_watch = quic_bridge_->receiver_demuxer->SetDefaultMessageTypeWatch( msgs::Type::kPresentationTerminationRequest, &mock_callback); msgs::PresentationTerminationRequest termination_request; msgs::Type msg_type; EXPECT_CALL(mock_callback, OnStreamMessage(_, _, _, _, _, _)) .WillOnce(Invoke([&termination_request, &msg_type]( uint64_t endpoint_id, uint64_t cid, msgs::Type message_type, const uint8_t* buffer, size_t buffer_size, Clock::time_point now) { msg_type = message_type; ssize_t result = msgs::DecodePresentationTerminationRequest( buffer, buffer_size, &termination_request); return result; })); connection->Terminate(TerminationReason::kControllerTerminateCalled); quic_bridge_->RunTasksUntilIdle(); ASSERT_EQ(msgs::Type::kPresentationTerminationRequest, msg_type); msgs::PresentationTerminationResponse termination_response; termination_response.request_id = termination_request.request_id; termination_response.result = msgs::PresentationTerminationResponse_result::kSuccess; SendTerminationResponse(termination_response); // TODO(btolsch): Check OnTerminated of other connections when reconnect // lands. quic_bridge_->RunTasksUntilIdle(); } TEST_F(ControllerTest, TerminatePresentationFromReceiver) { MockMessageCallback mock_callback; MockConnectionDelegate mock_connection_delegate; std::unique_ptr connection; StartPresentation(&mock_callback, &mock_connection_delegate, &connection); msgs::PresentationTerminationEvent termination_event; termination_event.presentation_id = connection->presentation_info().id; termination_event.reason = msgs::PresentationTerminationEvent_reason::kReceiverCalledTerminate; SendTerminationEvent(termination_event); EXPECT_CALL(mock_connection_delegate, OnTerminated()); quic_bridge_->RunTasksUntilIdle(); } TEST_F(ControllerTest, CloseConnection) { MockMessageCallback mock_callback; MockConnectionDelegate mock_connection_delegate; std::unique_ptr connection; StartPresentation(&mock_callback, &mock_connection_delegate, &connection); MessageDemuxer::MessageWatch close_request_watch = quic_bridge_->receiver_demuxer->SetDefaultMessageTypeWatch( msgs::Type::kPresentationConnectionCloseRequest, &mock_callback); msgs::PresentationConnectionCloseRequest close_request; ExpectCloseRequest(&mock_callback, &close_request, connection.get()); msgs::PresentationConnectionCloseResponse close_response; close_response.request_id = close_request.request_id; close_response.result = msgs::PresentationConnectionCloseResponse_result::kSuccess; SendCloseResponse(close_response); quic_bridge_->RunTasksUntilIdle(); } TEST_F(ControllerTest, Reconnect) { MockMessageCallback mock_callback; NiceMock mock_connection_delegate; std::unique_ptr connection; StartPresentation(&mock_callback, &mock_connection_delegate, &connection); MessageDemuxer::MessageWatch close_request_watch = quic_bridge_->receiver_demuxer->SetDefaultMessageTypeWatch( msgs::Type::kPresentationConnectionCloseRequest, &mock_callback); msgs::PresentationConnectionCloseRequest close_request; ExpectCloseRequest(&mock_callback, &close_request, connection.get()); msgs::PresentationConnectionCloseResponse close_response; close_response.request_id = close_request.request_id; close_response.result = msgs::PresentationConnectionCloseResponse_result::kSuccess; SendCloseResponse(close_response); quic_bridge_->RunTasksUntilIdle(); MessageDemuxer::MessageWatch connection_open_watch = quic_bridge_->receiver_demuxer->SetDefaultMessageTypeWatch( msgs::Type::kPresentationConnectionOpenRequest, &mock_callback); msgs::PresentationConnectionOpenRequest open_request; MockRequestDelegate reconnect_delegate; Controller::ConnectRequest reconnect_request = controller_->ReconnectConnection(std::move(connection), &reconnect_delegate); ASSERT_TRUE(reconnect_request); ssize_t decode_result = -1; msgs::Type msg_type; EXPECT_CALL(mock_callback, OnStreamMessage(_, _, _, _, _, _)) .WillOnce(Invoke([&open_request, &msg_type, &decode_result]( uint64_t endpoint_id, uint64_t cid, msgs::Type message_type, const uint8_t* buffer, size_t buffer_size, Clock::time_point now) { msg_type = message_type; decode_result = msgs::DecodePresentationConnectionOpenRequest( buffer, buffer_size, &open_request); return decode_result; })); quic_bridge_->RunTasksUntilIdle(); ASSERT_FALSE(connection); ASSERT_EQ(msg_type, msgs::Type::kPresentationConnectionOpenRequest); ASSERT_GT(decode_result, 0); msgs::PresentationConnectionOpenResponse open_response; open_response.request_id = open_request.request_id; open_response.connection_id = 17; open_response.result = msgs::PresentationConnectionOpenResponse_result::kSuccess; SendOpenResponse(open_response); EXPECT_CALL(reconnect_delegate, OnConnectionMock(_)) .WillOnce(Invoke([&connection](std::unique_ptr& c) { connection = std::move(c); })); EXPECT_CALL(mock_connection_delegate, OnConnected()); quic_bridge_->RunTasksUntilIdle(); ASSERT_TRUE(connection); EXPECT_EQ(connection->state(), Connection::State::kConnected); } } // namespace osp } // namespace openscreen