JD2022-TU1/main/unittests/core/HttpServer/WebSocket/WebSocketConnectionBase.cpp

752 lines
31 KiB
C++

#include "precompiled_unittests_core.h"
#include "core/HttpServer/WebSocket/WebSocketConnectionBase.h"
#include "../../AdaptersInterfaces/System_Adapter_Empty.h"
// windows only at the moment
#ifdef ITF_WINDOWS
#include "core/HttpServer/WebSocket/WebSocketTextMessage.h"
#include "core/HttpServer/WebSocket/WebSocketBinaryMessage.h"
#include "core/HttpServer/WebSocket/WebSocketCloseMessage.h"
#include "HttpServer/HttpConnectionMock.h"
#include "debug/warning/WarningManagerStub.h"
using ::testing::_;
namespace ITF
{
class WebSocketConnectionBase_PrivateMembersAccessor
{
public:
explicit WebSocketConnectionBase_PrivateMembersAccessor(WebSocketConnectionBase* _source)
: m_source(_source) {}
void onOpen() { m_source->onOpen(); }
void onClose() { m_source->onClose(); }
void onMessage(WebSocketConnectionBase::WebSocketTextMessageSink& _textMsg) { m_source->onMessage(_textMsg); }
void onMessage(WebSocketConnectionBase::WebSocketBinaryMessageSink& _binaryMsg) { m_source->onMessage(_binaryMsg); }
void onMessage(WebSocketConnectionBase::WebSocketCloseMessageSink& _closeMsg) { m_source->onMessage(_closeMsg); }
bool onPoll(const HttpConnection& _conn) { return m_source->onPoll(_conn); }
WebSocketConnectionBase::Queues& getIncomingQueues() { return m_source->m_incomingQueues; }
WebSocketConnectionBase::Queues& getOutgoingQueues() { return m_source->m_outgoingQueues; }
WebSocketConnectionBase::State getState() const { return m_source->m_state; }
WebSocketConnectionBase::AcceptedMsgTypes getAcceptedMsgTypes() const { return m_source->m_acceptedMsgTypes; }
private:
WebSocketConnectionBasePtr m_source;
};
// construction test
// expected: connection in 'connecting' state
// expected: accepted messages type set correctly
TEST(WebSocketConnectionBase, test_construction)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
WebSocketConnectionBase::State expectedState = WebSocketConnectionBase::State_Connecting;
WebSocketConnectionBase::AcceptedMsgTypes expectedAcceptedMsgTypes = WebSocketConnectionBase::AcceptedMsgTypes_All;
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(expectedAcceptedMsgTypes));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
EXPECT_EQ(expectedState, testMemberAccessor.getState());
EXPECT_EQ(expectedAcceptedMsgTypes, testMemberAccessor.getAcceptedMsgTypes());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// validate that after onOpen() call, the connection is in 'Open' state.
TEST(WebSocketConnectionBase, test_onOpen_switchesToOpenState)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
WebSocketConnectionBase::State expectedState = WebSocketConnectionBase::State_Open;
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen();
EXPECT_EQ(expectedState, testMemberAccessor.getState());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// validate that after onOpen() and onClose() calls, the connection is in 'Closed' state.
TEST(WebSocketConnectionBase, test_onClose_switchesToClosedState)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
WebSocketConnectionBase::State expectedState = WebSocketConnectionBase::State_Closed;
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen();
testMemberAccessor.onClose();
EXPECT_EQ(expectedState, testMemberAccessor.getState());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
//--------------------
// send() tests
//--------------------
// send text message, all ok
TEST(WebSocketConnectionBase, test_sendTextMessage_Ok)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
String8 msgContent("TextMessage");
size_t expectedMsgCount = 1;
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // pass to open state
EXPECT_TRUE(conn->send(msgContent) == btrue);
EXPECT_EQ(expectedMsgCount, testMemberAccessor.getOutgoingQueues().m_textMsgQueue.size());
WebSocketTextMessage* msg = nullptr;
testMemberAccessor.getOutgoingQueues().m_textMsgQueue.pop(msg);
ASSERT_NE(nullptr, msg);
EXPECT_STREQ(msgContent.cStr(), msg->getContent());
delete msg;
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send text message, failed because not in open state
TEST(WebSocketConnectionBase, test_sendTextMessage_FailedNotOpen)
{
String8 msgContent("TextMessage");
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
EXPECT_TRUE(conn->send(msgContent) == bfalse);
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send text message, failed because it does not accept text messages
TEST(WebSocketConnectionBase, test_sendTextMessage_FailedDoesNotAcceptText)
{
String8 msgContent("TextMessage");
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // pass to open state
EXPECT_TRUE(conn->send(msgContent) == bfalse);
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send text message, failed because queue full
TEST(WebSocketConnectionBase, test_sendTextMessage_FailedQueueFull)
{
String8 msgContent("TextMessage");
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
const size_t queueMaxSize = 0; // queue will be immediately full
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text, queueMaxSize));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // pass to open state
EXPECT_TRUE(conn->send(msgContent) == bfalse);
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send binary message, all ok
TEST(WebSocketConnectionBase, test_sendBinaryMessage_Ok)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
size_t expectedMsgCount = 1;
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // pass to open state
EXPECT_TRUE(conn->send(msgContent, msgContentLen) == btrue);
EXPECT_EQ(expectedMsgCount, testMemberAccessor.getOutgoingQueues().m_binaryMsgQueue.size());
WebSocketBinaryMessage* msg = nullptr;
testMemberAccessor.getOutgoingQueues().m_binaryMsgQueue.pop(msg);
ASSERT_NE(nullptr, msg);
ASSERT_EQ(msgContentLen, msg->getContentLen());
EXPECT_EQ(0, memcmp(msgContent, msg->getContent(), msgContentLen));
delete msg;
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send binary message, failed because not in open state
TEST(WebSocketConnectionBase, test_sendBinaryMessage_FailedNotOpen)
{
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
EXPECT_TRUE(conn->send(msgContent, msgContentLen) == bfalse);
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send binary message, failed because it does not accept binary messages
TEST(WebSocketConnectionBase, test_sendBinaryMessage_FailedDoesNotAcceptBinary)
{
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // pass to open state
EXPECT_TRUE(conn->send(msgContent, msgContentLen) == bfalse);
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send binary message, failed because queue full
TEST(WebSocketConnectionBase, test_sendBinaryMessage_FailedQueueFull)
{
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
const size_t queueMaxSize = 0; // queue will be immediately full
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary, queueMaxSize));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // pass to open state
EXPECT_TRUE(conn->send(msgContent, msgContentLen) == bfalse);
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
//--------------------
// close() tests
//--------------------
// send close message, all ok
TEST(WebSocketConnectionBase, test_close_Ok)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
const u16 code = 1000;
const char* reason = "CLOSE_NORMAL";
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
conn->close(code, reason);
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// send close message, ignored, close message already queued
TEST(WebSocketConnectionBase, test_close_Ignored)
{
const u16 code = 1000;
const char* reason = "CLOSE_NORMAL";
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
conn->close(1001, "BlaBla"); // queue first close message
// second close is ignored
conn->close(code, reason);
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_NE(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STRNE(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
//--------------------
// onMessage() tests
//--------------------
// receive text message, everything goes ok
// message should appear in the income queue
TEST(WebSocketConnectionBase, test_onMessage_Text_ok)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
String8 msgContent = "Text message";
const size_t msgCountInQueue = 1;
WebSocketConnectionBase::WebSocketTextMessageSink msgSink(new WebSocketTextMessage(msgContent.cStr (), msgContent.getLen ()));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_EQ(nullptr, msgSink.get()); // sink is empty (message has been moved to the queue)
ASSERT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_textMsgQueue.size());
WebSocketTextMessage* msg = nullptr;
testMemberAccessor.getIncomingQueues().m_textMsgQueue.pop(msg);
ASSERT_NE(nullptr, msg);
EXPECT_STREQ(msgContent.cStr(), msg->getContent());
delete msg;
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive text message, rejected because connection is not in opened state
// close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onMessage_Text_FailedNotOpened)
{
String8 msgContent = "Text message";
const size_t msgCountInQueue = 0;
const u16 code = 1002;
const char* reason = "CLOSE_PROTOCOL_ERROR";
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketTextMessageSink msgSink(new WebSocketTextMessage(msgContent.cStr (), msgContent.getLen ()));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
EXPECT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_textMsgQueue.size()); // no messages in the queue
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive text message, rejected because incoming queue is full
// close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onMessage_Text_FailedFull)
{
String8 msgContent = "Text message";
const size_t msgCountInQueue = 0;
const u16 code = 1011;
const char* reason = "CLOSE_UNEXPECTED_COND";
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketTextMessageSink msgSink(new WebSocketTextMessage(msgContent.cStr (), msgContent.getLen ()));
const size_t queueMaxSize = 0; // queue will be immediately full
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text, queueMaxSize));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen();
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
EXPECT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_textMsgQueue.size()); // no messages in the queue
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive text message, rejected because connection does not accept text messages
// close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onMessage_Text_FailedTextNotAccepted)
{
String8 msgContent = "Text message";
const size_t msgCountInQueue = 0;
const u16 code = 1003;
const char* reason = "CLOSE_UNSUPPORTED";
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketTextMessageSink msgSink(new WebSocketTextMessage(msgContent.cStr (), msgContent.getLen ()));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen();
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
EXPECT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_textMsgQueue.size()); // no messages in the queue
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive binary message, everything goes ok
// message should appear in the income queue
TEST(WebSocketConnectionBase, test_onMessage_Binary_ok)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
const size_t msgCountInQueue = 1;
WebSocketConnectionBase::WebSocketBinaryMessageSink msgSink(new WebSocketBinaryMessage(msgContent, msgContentLen));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_EQ(nullptr, msgSink.get()); // sink is empty (message has been moved to the queue)
ASSERT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_binaryMsgQueue.size());
WebSocketBinaryMessage* msg = nullptr;
testMemberAccessor.getIncomingQueues().m_binaryMsgQueue.pop(msg);
ASSERT_NE(nullptr, msg);
ASSERT_EQ(msgContentLen, msg->getContentLen());
EXPECT_EQ(0, memcmp(msgContent, msg->getContent(), msgContentLen));
delete msg;
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive binary message, rejected because connection is not in opened state
// close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onMessage_Binary_FailedNotOpened)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
const size_t msgCountInQueue = 0;
const u16 code = 1002;
const char* reason = "CLOSE_PROTOCOL_ERROR";
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketBinaryMessageSink msgSink(new WebSocketBinaryMessage(msgContent, msgContentLen));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
EXPECT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_binaryMsgQueue.size()); // no messages in the queue
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive binary message, rejected because incoming queue is full
// close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onMessage_Binary_FailedFull)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
const size_t msgCountInQueue = 0;
const u16 code = 1011;
const char* reason = "CLOSE_UNEXPECTED_COND";
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketBinaryMessageSink msgSink(new WebSocketBinaryMessage(msgContent, msgContentLen));
const size_t queueMaxSize = 0; // queue will be immediately full
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Binary, queueMaxSize));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen();
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
EXPECT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_binaryMsgQueue.size()); // no messages in the queue
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive binary message, rejected because connection does not accept binary messages
// close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onMessage_Binary_FailedBinaryNotAccepted)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
u8 msgContent[] = { 0x2, 0x3, 0x4, 0x5 };
size_t msgContentLen = ITF_ARRAY_SIZE(msgContent);
const size_t msgCountInQueue = 0;
const u16 code = 1003;
const char* reason = "CLOSE_UNSUPPORTED";
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketBinaryMessageSink msgSink(new WebSocketBinaryMessage(msgContent, msgContentLen));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_Text));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen();
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
EXPECT_EQ(msgCountInQueue, testMemberAccessor.getIncomingQueues().m_binaryMsgQueue.size()); // no messages in the queue
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
//--------------------
// onClose() tests
//--------------------
// receive close message, everything goes ok
// message should appear in the income queue (m_closeMsg member)
TEST(WebSocketConnectionBase, test_onClose_ok)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
const u16 code = 1000;
const char* reason = "CLOSE_NORMAL";
WebSocketConnectionBase::WebSocketCloseMessageSink msgSink(new WebSocketCloseMessage(code, reason));
WebSocketConnectionBase::State expectedStateAfterOnClose = WebSocketConnectionBase::State_Closing;
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
EXPECT_EQ(expectedStateAfterOnClose, testMemberAccessor.getState());
ASSERT_EQ(nullptr, msgSink.get()); // sink is empty (message has been moved to the queue)
ASSERT_NE(nullptr, testMemberAccessor.getIncomingQueues().m_closeMsg.get());
EXPECT_EQ(code, testMemberAccessor.getIncomingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason, testMemberAccessor.getIncomingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive close message, failed, connection not opened
// outgoing close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onClose_FailedNotOpen)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
const u16 code = 1000;
const char* reason = "CLOSE_NORMAL";
const u16 failCode = 1002;
const char* failReason = "CLOSE_PROTOCOL_ERROR";
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketCloseMessageSink msgSink(new WebSocketCloseMessage(code, reason));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink);
ASSERT_NE(nullptr, msgSink.get()); // sink is not empty
ASSERT_EQ(nullptr, testMemberAccessor.getIncomingQueues().m_closeMsg.get()); // no close message posted
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(failCode, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(failReason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// receive close message, failed, already received close command
// outgoing close command has been issued to close the connection
TEST(WebSocketConnectionBase, test_onClose_FailedAlreadyReceivedCloseCmd)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
const u16 code1 = 1000;
const char* reason1 = "CLOSE_NORMAL";
const u16 code2 = 1001;
const char* reason2 = "CLOSE_GOING_AWAY";
const u16 failCode = 1002;
const char* failReason = "CLOSE_PROTOCOL_ERROR";
CREATE_CLASS_SINGLETON(WarningManagerStub);
WebSocketConnectionBase::WebSocketCloseMessageSink msgSink1(new WebSocketCloseMessage(code1, reason1));
WebSocketConnectionBase::WebSocketCloseMessageSink msgSink2(new WebSocketCloseMessage(code2, reason2));
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
testMemberAccessor.onMessage(msgSink1); // first call to close (success)
// test connection via call to testMemberAccessor
testMemberAccessor.onMessage(msgSink2);
ASSERT_NE(nullptr, msgSink2.get()); // sink is not empty
ASSERT_NE(nullptr, testMemberAccessor.getIncomingQueues().m_closeMsg.get()); // close message exists (first one)
// first command remained in the queue
EXPECT_EQ(code1, testMemberAccessor.getIncomingQueues().m_closeMsg->getCode());
EXPECT_STREQ(reason1, testMemberAccessor.getIncomingQueues().m_closeMsg->getReason().cStr());
// correct close command issued
ASSERT_NE(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
EXPECT_EQ(failCode, testMemberAccessor.getOutgoingQueues().m_closeMsg->getCode());
EXPECT_STREQ(failReason, testMemberAccessor.getOutgoingQueues().m_closeMsg->getReason().cStr());
DESTROY_CLASS_SINGLETON(WarningManagerStub);
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
//--------------------
// onPoll() tests
//--------------------
// onPoll sends a text message
// expected: HttpConnection::wsVPrintf is called with TEXT opcode
TEST(WebSocketConnectionBase, test_onPoll_sendText)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
String8 msg("Text message");
::testing::StrictMock<HttpConnectionMock> httpConnection;
EXPECT_CALL(httpConnection, wsVPrintf(HttpConnection::WebSocketOpCode_TEXT, _, _)).Times(1);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
conn->send(msg); // queue text message
testMemberAccessor.onPoll(httpConnection);
EXPECT_TRUE(testMemberAccessor.getOutgoingQueues().m_textMsgQueue.empty());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// onPoll sends a binary message
// expected: HttpConnection::wsWrite is called with BINARY opcode
TEST(WebSocketConnectionBase, test_onPoll_sendBinary)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
u8 msg[] = { 0x1, 0x2, 0x3, 0x4 };
const size_t msgLen = ITF_ARRAY_SIZE(msg);
::testing::StrictMock<HttpConnectionMock> httpConnection;
EXPECT_CALL(httpConnection, wsWrite(HttpConnection::WebSocketOpCode_BINARY, _, msgLen)).Times(1);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
conn->send(msg, msgLen); // queue binary message
testMemberAccessor.onPoll(httpConnection);
EXPECT_TRUE(testMemberAccessor.getOutgoingQueues().m_binaryMsgQueue.empty());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
// onPoll sends a close message
// expected: HttpConnection::wsWrite is called with CLOSE opcode
TEST(WebSocketConnectionBase, test_onPoll_sendClose)
{
CREATE_CLASS_SINGLETON(SystemAdapter_Empty);
const u16 code = 1000;
const char* reason = "CLOSE_NORMAL";
const size_t closeMsgSize = sizeof(code) + strlen(reason);
const WebSocketConnectionBase::State expectedState = WebSocketConnectionBase::State_Open; // the connection is stil open if we just sent a close message
::testing::StrictMock<HttpConnectionMock> httpConnection;
EXPECT_CALL(httpConnection, wsWrite(HttpConnection::WebSocketOpCode_CONNECTION_CLOSE, _, closeMsgSize)).Times(1);
WebSocketConnectionBasePtr conn(new WebSocketConnectionBase(WebSocketConnectionBase::AcceptedMsgTypes_All));
WebSocketConnectionBase_PrivateMembersAccessor testMemberAccessor(conn.getPointer());
testMemberAccessor.onOpen(); // open connection
conn->close(code, reason); // queue close message
testMemberAccessor.onPoll(httpConnection);
EXPECT_EQ(expectedState, testMemberAccessor.getState());
EXPECT_EQ(nullptr, testMemberAccessor.getOutgoingQueues().m_closeMsg.get());
DESTROY_CLASS_SINGLETON(SystemAdapter_Empty);
}
} // namespace ITF
// windows only at the moment
#endif // ITF_WINDOWS