#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 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 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 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