blob: 343ea4d888af1980c43c4153ec6bfbcf86a18d5c [file] [log] [blame]
// 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 <string>
#include <vector>
#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> connection) override {
OnConnectionMock(connection);
}
MOCK_METHOD1(OnConnectionMock, void(std::unique_ptr<Connection>& connection));
MOCK_METHOD1(OnError, void(const Error& error));
};
} // namespace
class ControllerTest : public ::testing::Test {
public:
ControllerTest() {
fake_clock_ = std::make_unique<FakeClock>(
Clock::time_point(std::chrono::seconds(11111)));
task_runner_ = std::make_unique<FakeTaskRunner>(fake_clock_.get());
quic_bridge_ =
std::make_unique<FakeQuicBridge>(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<ServiceListenerImpl>(&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<Controller>(FakeClock::now);
ON_CALL(quic_bridge_->mock_server_observer, OnIncomingConnectionMock(_))
.WillByDefault(
Invoke([this](std::unique_ptr<ProtocolConnection>& 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<ProtocolConnection> 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<ProtocolConnection> 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<ProtocolConnection> 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<ProtocolConnection> 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<ProtocolConnection> 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<ProtocolConnection> 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<ProtocolConnection> 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>* 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<Connection>& c) {
*connection = std::move(c);
}));
EXPECT_CALL(*mock_connection_delegate, OnConnected());
quic_bridge_->RunTasksUntilIdle();
ASSERT_TRUE(*connection);
}
std::unique_ptr<FakeClock> fake_clock_;
std::unique_ptr<FakeTaskRunner> task_runner_;
MessageDemuxer::MessageWatch availability_watch_;
MockMessageCallback mock_callback_;
std::unique_ptr<FakeQuicBridge> quic_bridge_;
MockServiceListenerDelegate mock_listener_delegate_;
std::unique_ptr<Controller> controller_;
ServiceInfo receiver_info1;
MockReceiverObserver mock_receiver_observer_;
uint64_t controller_endpoint_id_{0};
};
TEST_F(ControllerTest, ReceiverWatchMoves) {
std::vector<std::string> 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<MockConnectionDelegate> mock_connection_delegate;
std::unique_ptr<Connection> connection;
StartPresentation(&mock_callback, &mock_connection_delegate, &connection);
}
TEST_F(ControllerTest, TerminatePresentationFromController) {
MockMessageCallback mock_callback;
MockConnectionDelegate mock_connection_delegate;
std::unique_ptr<Connection> 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> 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> 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<MockConnectionDelegate> mock_connection_delegate;
std::unique_ptr<Connection> 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<Connection>& 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