752 lines
31 KiB
C++
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
|
|
|