diff options
Diffstat (limited to 'cast/streaming/session_messenger_unittest.cc')
-rw-r--r-- | cast/streaming/session_messenger_unittest.cc | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/cast/streaming/session_messenger_unittest.cc b/cast/streaming/session_messenger_unittest.cc new file mode 100644 index 00000000..ce2f1655 --- /dev/null +++ b/cast/streaming/session_messenger_unittest.cc @@ -0,0 +1,511 @@ +// 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/streaming/session_messenger.h" + +#include "cast/streaming/testing/message_pipe.h" +#include "cast/streaming/testing/simple_message_port.h" +#include "gtest/gtest.h" +#include "platform/test/fake_clock.h" +#include "platform/test/fake_task_runner.h" + +namespace openscreen { +namespace cast { + +using ::testing::ElementsAre; + +namespace { + +constexpr char kSenderId[] = "sender-12345"; +constexpr char kReceiverId[] = "receiver-12345"; + +// Generally the messages are inlined below, with the exception of the Offer, +// simply because it is massive. +Offer kExampleOffer{ + CastMode::kMirroring, + {AudioStream{Stream{0, + Stream::Type::kAudioSource, + 2, + RtpPayloadType::kAudioOpus, + 12344442, + std::chrono::milliseconds{2000}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + false, + "", + 48000}, + AudioCodec::kOpus, 1400}}, + {VideoStream{Stream{1, + Stream::Type::kVideoSource, + 1, + RtpPayloadType::kVideoVp8, + 12344444, + std::chrono::milliseconds{2000}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + false, + "", + 90000}, + VideoCodec::kVp8, + SimpleFraction{30, 1}, + 3000000, + "", + "", + "", + {Resolution{640, 480}}, + ""}}}; + +struct SessionMessageStore { + public: + SenderSessionMessenger::ReplyCallback GetReplyCallback() { + return [this](ReceiverMessage message) { + receiver_messages.push_back(std::move(message)); + }; + } + + ReceiverSessionMessenger::RequestCallback GetRequestCallback() { + return [this](SenderMessage message) { + sender_messages.push_back(std::move(message)); + }; + } + + SessionMessenger::ErrorCallback GetErrorCallback() { + return [this](Error error) { errors.push_back(std::move(error)); }; + } + + std::vector<SenderMessage> sender_messages; + std::vector<ReceiverMessage> receiver_messages; + std::vector<Error> errors; +}; +} // namespace + +class SessionMessengerTest : public ::testing::Test { + public: + SessionMessengerTest() + : clock_{Clock::now()}, + task_runner_(&clock_), + message_store_(), + pipe_(kSenderId, kReceiverId), + receiver_messenger_(pipe_.right(), + kReceiverId, + message_store_.GetErrorCallback()), + sender_messenger_(pipe_.left(), + kSenderId, + kReceiverId, + message_store_.GetErrorCallback(), + &task_runner_) + + {} + + void SetUp() override { + sender_messenger_.SetHandler(ReceiverMessage::Type::kRpc, + message_store_.GetReplyCallback()); + receiver_messenger_.SetHandler(SenderMessage::Type::kOffer, + message_store_.GetRequestCallback()); + receiver_messenger_.SetHandler(SenderMessage::Type::kGetCapabilities, + message_store_.GetRequestCallback()); + receiver_messenger_.SetHandler(SenderMessage::Type::kRpc, + message_store_.GetRequestCallback()); + } + + protected: + FakeClock clock_; + FakeTaskRunner task_runner_; + SessionMessageStore message_store_; + MessagePipe pipe_; + ReceiverSessionMessenger receiver_messenger_; + SenderSessionMessenger sender_messenger_; + + std::vector<Error> receiver_errors_; + std::vector<Error> sender_errors_; +}; + +TEST_F(SessionMessengerTest, RpcMessaging) { + static const std::vector<uint8_t> kSenderMessage{1, 2, 3, 4, 5}; + static const std::vector<uint8_t> kReceiverResponse{6, 7, 8, 9}; + ASSERT_TRUE( + sender_messenger_ + .SendOutboundMessage(SenderMessage{SenderMessage::Type::kRpc, 123, + true /* valid */, kSenderMessage}) + .ok()); + + ASSERT_EQ(1u, message_store_.sender_messages.size()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); + EXPECT_EQ(SenderMessage::Type::kRpc, message_store_.sender_messages[0].type); + ASSERT_TRUE(message_store_.sender_messages[0].valid); + EXPECT_EQ(kSenderMessage, absl::get<std::vector<uint8_t>>( + message_store_.sender_messages[0].body)); + + message_store_.sender_messages.clear(); + ASSERT_TRUE( + receiver_messenger_ + .SendMessage(ReceiverMessage{ReceiverMessage::Type::kRpc, 123, + true /* valid */, kReceiverResponse}) + .ok()); + + ASSERT_TRUE(message_store_.sender_messages.empty()); + ASSERT_EQ(1u, message_store_.receiver_messages.size()); + EXPECT_EQ(ReceiverMessage::Type::kRpc, + message_store_.receiver_messages[0].type); + EXPECT_TRUE(message_store_.receiver_messages[0].valid); + EXPECT_EQ(kReceiverResponse, absl::get<std::vector<uint8_t>>( + message_store_.receiver_messages[0].body)); +} + +TEST_F(SessionMessengerTest, CapabilitiesMessaging) { + ASSERT_TRUE( + sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kGetCapabilities, + 1337, true /* valid */}, + ReceiverMessage::Type::kCapabilitiesResponse, + message_store_.GetReplyCallback()) + .ok()); + + ASSERT_EQ(1u, message_store_.sender_messages.size()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); + EXPECT_EQ(SenderMessage::Type::kGetCapabilities, + message_store_.sender_messages[0].type); + EXPECT_TRUE(message_store_.sender_messages[0].valid); + + message_store_.sender_messages.clear(); + ASSERT_TRUE(receiver_messenger_ + .SendMessage(ReceiverMessage{ + ReceiverMessage::Type::kCapabilitiesResponse, 1337, + true /* valid */, + ReceiverCapability{ + 47, {MediaCapability::kAac, MediaCapability::k4k}}}) + .ok()); + + ASSERT_TRUE(message_store_.sender_messages.empty()); + ASSERT_EQ(1u, message_store_.receiver_messages.size()); + EXPECT_EQ(ReceiverMessage::Type::kCapabilitiesResponse, + message_store_.receiver_messages[0].type); + EXPECT_TRUE(message_store_.receiver_messages[0].valid); + + const auto& capability = + absl::get<ReceiverCapability>(message_store_.receiver_messages[0].body); + EXPECT_EQ(47, capability.remoting_version); + EXPECT_THAT(capability.media_capabilities, + ElementsAre(MediaCapability::kAac, MediaCapability::k4k)); +} + +TEST_F(SessionMessengerTest, OfferAnswerMessaging) { + ASSERT_TRUE(sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kOffer, 42, + true /* valid */, kExampleOffer}, + ReceiverMessage::Type::kAnswer, + message_store_.GetReplyCallback()) + .ok()); + + ASSERT_EQ(1u, message_store_.sender_messages.size()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); + EXPECT_EQ(SenderMessage::Type::kOffer, + message_store_.sender_messages[0].type); + EXPECT_TRUE(message_store_.sender_messages[0].valid); + message_store_.sender_messages.clear(); + + EXPECT_TRUE(receiver_messenger_ + .SendMessage(ReceiverMessage{ + ReceiverMessage::Type::kAnswer, 41, true /* valid */, + Answer{1234, {0, 1}, {12344443, 12344445}}}) + .ok()); + // A stale answer (for offer 41) should get ignored: + ASSERT_TRUE(message_store_.sender_messages.empty()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); + + ASSERT_TRUE(receiver_messenger_ + .SendMessage(ReceiverMessage{ + ReceiverMessage::Type::kAnswer, 42, true /* valid */, + Answer{1234, {0, 1}, {12344443, 12344445}}}) + .ok()); + EXPECT_TRUE(message_store_.sender_messages.empty()); + ASSERT_EQ(1u, message_store_.receiver_messages.size()); + EXPECT_EQ(ReceiverMessage::Type::kAnswer, + message_store_.receiver_messages[0].type); + EXPECT_TRUE(message_store_.receiver_messages[0].valid); + + const auto& answer = + absl::get<Answer>(message_store_.receiver_messages[0].body); + EXPECT_EQ(1234, answer.udp_port); + + EXPECT_THAT(answer.send_indexes, ElementsAre(0, 1)); + EXPECT_THAT(answer.ssrcs, ElementsAre(12344443, 12344445)); +} + +TEST_F(SessionMessengerTest, OfferAndReceiverError) { + ASSERT_TRUE(sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kOffer, 42, + true /* valid */, kExampleOffer}, + ReceiverMessage::Type::kAnswer, + message_store_.GetReplyCallback()) + .ok()); + + ASSERT_EQ(1u, message_store_.sender_messages.size()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); + EXPECT_EQ(SenderMessage::Type::kOffer, + message_store_.sender_messages[0].type); + EXPECT_TRUE(message_store_.sender_messages[0].valid); + message_store_.sender_messages.clear(); + + EXPECT_TRUE(receiver_messenger_ + .SendMessage(ReceiverMessage{ + ReceiverMessage::Type::kAnswer, 42, false /* valid */, + ReceiverError{123, "Something real bad happened"}}) + .ok()); + + EXPECT_TRUE(message_store_.sender_messages.empty()); + ASSERT_EQ(1u, message_store_.receiver_messages.size()); + EXPECT_EQ(ReceiverMessage::Type::kAnswer, + message_store_.receiver_messages[0].type); + EXPECT_FALSE(message_store_.receiver_messages[0].valid); + + const auto& error = + absl::get<ReceiverError>(message_store_.receiver_messages[0].body); + EXPECT_EQ(123, error.code); + EXPECT_EQ("Something real bad happened", error.description); +} + +TEST_F(SessionMessengerTest, UnexpectedMessagesAreIgnored) { + EXPECT_FALSE(receiver_messenger_ + .SendMessage(ReceiverMessage{ + ReceiverMessage::Type::kCapabilitiesResponse, 3123, + true /* valid */, + ReceiverCapability{2, {MediaCapability::kH264}}}) + .ok()); + + // The message gets dropped and thus won't be in the store. + EXPECT_TRUE(message_store_.sender_messages.empty()); + EXPECT_TRUE(message_store_.receiver_messages.empty()); +} + +TEST_F(SessionMessengerTest, UnknownSenderMessageTypesDontGetSent) { + EXPECT_DEATH(sender_messenger_ + .SendOutboundMessage(SenderMessage{ + SenderMessage::Type::kUnknown, 123, true /* valid */}) + .ok(), + ".*Trying to send an unknown message is a developer error.*"); +} + +TEST_F(SessionMessengerTest, UnknownReceiverMessageTypesDontGetSent) { + ASSERT_TRUE(sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kOffer, 42, + true /* valid */, kExampleOffer}, + ReceiverMessage::Type::kAnswer, + message_store_.GetReplyCallback()) + .ok()); + + EXPECT_DEATH(receiver_messenger_ + .SendMessage(ReceiverMessage{ReceiverMessage::Type::kUnknown, + 3123, true /* valid */}) + .ok(), + ".*Trying to send an unknown message is a developer error.*"); +} + +TEST_F(SessionMessengerTest, ReceiverHandlesUnknownMessageType) { + pipe_.right()->ReceiveMessage(kCastWebrtcNamespace, R"({ + "type": "GET_VIRTUAL_REALITY", + "seqNum": 31337 + })"); + ASSERT_TRUE(message_store_.errors.empty()); +} + +TEST_F(SessionMessengerTest, SenderHandlesUnknownMessageType) { + // The behavior on the sender side is a little more interesting: we + // test elsewhere that messages with the wrong sequence number are ignored, + // here if the type is unknown but the message contains a valid sequence + // number we just treat it as a bad response/same as a timeout. + ASSERT_TRUE(sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kOffer, 42, + true /* valid */, kExampleOffer}, + ReceiverMessage::Type::kAnswer, + message_store_.GetReplyCallback()) + .ok()); + pipe_.left()->ReceiveMessage(kCastWebrtcNamespace, R"({ + "type": "ANSWER_VERSION_2", + "seqNum": 42 + })"); + + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_EQ(1u, message_store_.receiver_messages.size()); + ASSERT_EQ(ReceiverMessage::Type::kUnknown, + message_store_.receiver_messages[0].type); + ASSERT_EQ(false, message_store_.receiver_messages[0].valid); +} + +TEST_F(SessionMessengerTest, SenderHandlesMessageMissingSequenceNumber) { + ASSERT_TRUE( + sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kGetCapabilities, 42, + true /* valid */}, + ReceiverMessage::Type::kCapabilitiesResponse, + message_store_.GetReplyCallback()) + .ok()); + pipe_.left()->ReceiveMessage(kCastWebrtcNamespace, R"({ + "capabilities": { + "keySystems": [], + "mediaCaps": ["video"] + }, + "result": "ok", + "type": "CAPABILITIES_RESPONSE" + })"); + + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); +} + +TEST_F(SessionMessengerTest, ReceiverCannotSendFirst) { + const Error error = receiver_messenger_.SendMessage(ReceiverMessage{ + ReceiverMessage::Type::kCapabilitiesResponse, 3123, true /* valid */, + ReceiverCapability{2, {MediaCapability::kAudio}}}); + + EXPECT_EQ(Error::Code::kInitializationFailure, error.code()); +} + +TEST_F(SessionMessengerTest, ErrorMessageLoggedIfTimeout) { + ASSERT_TRUE( + sender_messenger_ + .SendRequest(SenderMessage{SenderMessage::Type::kGetCapabilities, + 3123, true /* valid */}, + ReceiverMessage::Type::kCapabilitiesResponse, + message_store_.GetReplyCallback()) + .ok()); + + ASSERT_EQ(1u, message_store_.sender_messages.size()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); + + clock_.Advance(std::chrono::seconds(10)); + ASSERT_EQ(1u, message_store_.sender_messages.size()); + ASSERT_EQ(1u, message_store_.receiver_messages.size()); + EXPECT_EQ(3123, message_store_.receiver_messages[0].sequence_number); + EXPECT_EQ(ReceiverMessage::Type::kCapabilitiesResponse, + message_store_.receiver_messages[0].type); + EXPECT_FALSE(message_store_.receiver_messages[0].valid); +} + +TEST_F(SessionMessengerTest, ReceiverRejectsMessageFromWrongSender) { + SimpleMessagePort port(kReceiverId); + ReceiverSessionMessenger messenger(&port, kReceiverId, + message_store_.GetErrorCallback()); + messenger.SetHandler(SenderMessage::Type::kGetCapabilities, + message_store_.GetRequestCallback()); + + // The first message should be accepted since we don't have a set sender_id + // yet. + port.ReceiveMessage("sender-31337", kCastWebrtcNamespace, R"({ + "seqNum": 820263769, + "type": "GET_CAPABILITIES" + })"); + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_EQ(1u, message_store_.sender_messages.size()); + message_store_.sender_messages.clear(); + + // The second message should just be ignored. + port.ReceiveMessage("sender-42", kCastWebrtcNamespace, R"({ + "seqNum": 1234, + "type": "GET_CAPABILITIES" + })"); + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_TRUE(message_store_.sender_messages.empty()); + + // But the third message should be accepted again since it's from the + // first sender. + port.ReceiveMessage("sender-31337", kCastWebrtcNamespace, R"({ + "seqNum": 820263769, + "type": "GET_CAPABILITIES" + })"); + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_EQ(1u, message_store_.sender_messages.size()); +} + +TEST_F(SessionMessengerTest, SenderRejectsMessageFromWrongSender) { + SimpleMessagePort port(kReceiverId); + SenderSessionMessenger messenger(&port, kSenderId, kReceiverId, + message_store_.GetErrorCallback(), + &task_runner_); + + port.ReceiveMessage("receiver-31337", kCastWebrtcNamespace, R"({ + "seqNum": 12345, + "sessionId": 735189, + "type": "RPC", + "result": "ok", + "rpc": "SGVsbG8gZnJvbSB0aGUgQ2FzdCBSZWNlaXZlciE=" + })"); + + // The message should just be ignored. + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_TRUE(message_store_.sender_messages.empty()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); +} + +TEST_F(SessionMessengerTest, ReceiverRejectsMessagesWithoutHandler) { + SimpleMessagePort port(kReceiverId); + ReceiverSessionMessenger messenger(&port, kReceiverId, + message_store_.GetErrorCallback()); + messenger.SetHandler(SenderMessage::Type::kGetCapabilities, + message_store_.GetRequestCallback()); + + // The first message should be accepted since we don't have a set sender_id + // yet. + port.ReceiveMessage("sender-31337", kCastWebrtcNamespace, R"({ + "seqNum": 820263769, + "type": "GET_CAPABILITIES" + })"); + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_EQ(1u, message_store_.sender_messages.size()); + message_store_.sender_messages.clear(); + + // The second message should be rejected since it doesn't have a handler. + port.ReceiveMessage("sender-31337", kCastWebrtcNamespace, R"({ + "seqNum": 820263770, + "type": "RPC" + })"); + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_TRUE(message_store_.sender_messages.empty()); +} + +TEST_F(SessionMessengerTest, SenderRejectsMessagesWithoutHandler) { + SimpleMessagePort port(kReceiverId); + SenderSessionMessenger messenger(&port, kSenderId, kReceiverId, + message_store_.GetErrorCallback(), + &task_runner_); + + port.ReceiveMessage(kReceiverId, kCastWebrtcNamespace, R"({ + "seqNum": 12345, + "sessionId": 735189, + "type": "RPC", + "result": "ok", + "rpc": "SGVsbG8gZnJvbSB0aGUgQ2FzdCBSZWNlaXZlciE=" + })"); + + // The message should just be ignored. + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_TRUE(message_store_.sender_messages.empty()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); +} + +TEST_F(SessionMessengerTest, UnknownNamespaceMessagesGetDropped) { + pipe_.right()->ReceiveMessage("urn:x-cast:com.google.cast.virtualreality", + R"({ + "seqNum": 12345, + "sessionId": 735190, + "type": "RPC", + "rpc": "SGVsbG8gZnJvbSB0aGUgQ2FzdCBSZWNlaXZlciE=" + })"); + + pipe_.left()->ReceiveMessage("urn:x-cast:com.google.cast.virtualreality", R"({ + "seqNum": 12345, + "sessionId": 735190, + "type": "RPC", + "result": "ok", + "rpc": "SGVsbG8gZnJvbSB0aGUgQ2FzdCBTZW5kZXIh=" + })"); + + // The message should just be ignored. + ASSERT_TRUE(message_store_.errors.empty()); + ASSERT_TRUE(message_store_.sender_messages.empty()); + ASSERT_TRUE(message_store_.receiver_messages.empty()); +} + +} // namespace cast +} // namespace openscreen |