Patch by Jerry for issue 46:

- Unit test for sending file data from server to client.
- Removed singleton pattern from CSocketMultiplexer for easier unit testing.
- Incremented protocol version from 1.4 to 1.5 (new file chunk message).
- Storing pointer to CConfig instead of copying in CServer (so we can mock it).
- Created a common event queue for testing (timeout, quit event, etc).
- Fixed code style.
This commit is contained in:
Nick Bolton 2013-07-16 19:02:30 +00:00
parent 6f97f1d186
commit c368013f13
56 changed files with 830 additions and 165 deletions

View File

@ -180,6 +180,8 @@ REGISTER_EVENT(IScreen, shapeChanged)
REGISTER_EVENT(IScreen, clipboardGrabbed)
REGISTER_EVENT(IScreen, suspend)
REGISTER_EVENT(IScreen, resume)
REGISTER_EVENT(IScreen, fileChunkSending)
REGISTER_EVENT(IScreen, fileRecieveComplete)
//
// ISecondaryScreen

View File

@ -647,7 +647,9 @@ public:
m_shapeChanged(CEvent::kUnknown),
m_clipboardGrabbed(CEvent::kUnknown),
m_suspend(CEvent::kUnknown),
m_resume(CEvent::kUnknown) { }
m_resume(CEvent::kUnknown),
m_fileChunkSending(CEvent::kUnknown),
m_fileRecieveComplete(CEvent::kUnknown) { }
//! @name accessors
//@{
@ -683,11 +685,17 @@ public:
//! Get resume event type
/*!
Returns the suspend event type. This is sent whenever the system wakes
Returns the resume event type. This is sent whenever the system wakes
up or a user session is activated (fast user switching).
*/
CEvent::Type resume();
//! Sending a file chunk
CEvent::Type fileChunkSending();
//! Completed receiving a file
CEvent::Type fileRecieveComplete();
//@}
private:
@ -696,6 +704,8 @@ private:
CEvent::Type m_clipboardGrabbed;
CEvent::Type m_suspend;
CEvent::Type m_resume;
CEvent::Type m_fileChunkSending;
CEvent::Type m_fileRecieveComplete;
};
class ISecondaryScreenEvents : public CEventTypes {

View File

@ -30,11 +30,12 @@
#include "CLog.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
#include <cstring>
#include <cstdlib>
#include "CArch.h"
#include "IPlatformScreen.h"
#include "CCryptoStream.h"
#include <cstring>
#include <cstdlib>
#include <sstream>
//
// CClient
@ -83,6 +84,10 @@ CClient::CClient(IEventQueue* events,
getEventTarget(),
new TMethodEventJob<CClient>(this,
&CClient::handleGameDeviceFeedback));
m_events->adoptHandler(m_events->forIScreen().fileChunkSending(),
this,
new TMethodEventJob<CClient>(this,
&CClient::handleFileChunkSending));
}
CClient::~CClient()
@ -728,3 +733,33 @@ CClient::handleGameDeviceFeedback(const CEvent& event, void*)
m_server->onGameDeviceFeedback(info->m_id, info->m_m1, info->m_m2);
}
void
CClient::handleFileChunkSending(const CEvent& event, void*)
{
}
void
CClient::clearReceivedFileData()
{
m_receivedFileData.clear();
}
void
CClient::setExpectedFileSize(CString data)
{
std::istringstream iss(data);
iss >> m_expectedFileSize;
}
void
CClient::fileChunkReceived(CString data)
{
m_receivedFileData += data;
}
bool
CClient::isReceivedFileSizeValid()
{
return m_expectedFileSize == m_receivedFileData.size();
}

View File

@ -64,7 +64,7 @@ public:
~CClient();
#ifdef TEST_ENV
CClient() : m_mock(true), m_events(NULL) { }
CClient() : m_mock(true) { }
#endif
//! @name manipulators
@ -92,6 +92,18 @@ public:
//! Set crypto IV for decryption
virtual void setDecryptIv(const UInt8* iv);
//! Clears the file buffer
void clearReceivedFileData();
//! Set the expected size of receiving file
void setExpectedFileSize(CString data);
//! Received a chunk of file data
void fileChunkReceived(CString data);
//! Return true if recieved file size is valid
bool isReceivedFileSizeValid();
//@}
//! @name accessors
//@{
@ -175,6 +187,7 @@ private:
void handleResume(const CEvent& event, void*);
void handleGameDeviceTimingResp(const CEvent& event, void*);
void handleGameDeviceFeedback(const CEvent& event, void*);
void handleFileChunkSending(const CEvent&, void*);
public:
bool m_mock;
@ -199,6 +212,8 @@ private:
IEventQueue* m_events;
CCryptoStream* m_cryptoStream;
CCryptoOptions m_crypto;
std::size_t m_expectedFileSize;
CString m_receivedFileData;
};
#endif

View File

@ -309,6 +309,10 @@ CServerProxy::parseMessage(const UInt8* code)
cryptoIv();
}
else if (memcmp(code, kMsgDFileTransfer, 4) == 0) {
fileChunkReceived();
}
else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup
LOG((CLOG_DEBUG1 "recv close"));
@ -930,3 +934,29 @@ CServerProxy::infoAcknowledgment()
LOG((CLOG_DEBUG1 "recv info acknowledgment"));
m_ignoreMouse = false;
}
void CServerProxy::fileChunkReceived()
{
// parse
UInt8 mark;
CString content;
CProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content);
switch (mark) {
case '0':
LOG((CLOG_DEBUG2 "recv file data: file size = %s", content));
m_client->clearReceivedFileData();
m_client->setExpectedFileSize(content);
break;
case '1':
LOG((CLOG_DEBUG2 "recv file data: chunck size = %i", content.size()));
m_client->fileChunkReceived(content);
break;
case '2':
LOG((CLOG_DEBUG2 "file data transfer finished"));
m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), m_client));
break;
}
}

View File

@ -105,6 +105,7 @@ private:
void setOptions();
void queryInfo();
void infoAcknowledgment();
void fileChunkReceived();
private:
typedef EResult (CServerProxy::*MessageParser)(const UInt8*);

View File

@ -26,19 +26,19 @@
// CIpcClient
//
CIpcClient::CIpcClient(IEventQueue* events) :
CIpcClient::CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_serverAddress(CNetworkAddress(IPC_HOST, IPC_PORT)),
m_server(nullptr),
m_socket(events),
m_socket(events, socketMultiplexer),
m_events(events)
{
init();
}
CIpcClient::CIpcClient(IEventQueue* events, int port) :
CIpcClient::CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port) :
m_serverAddress(CNetworkAddress(IPC_HOST, port)),
m_server(nullptr),
m_socket(events),
m_socket(events, socketMultiplexer),
m_events(events)
{
init();

View File

@ -25,6 +25,7 @@
class CIpcServerProxy;
class CIpcMessage;
class IEventQueue;
class CSocketMultiplexer;
//! IPC client for communication between daemon and GUI.
/*!
@ -32,8 +33,8 @@ class IEventQueue;
*/
class CIpcClient {
public:
CIpcClient(IEventQueue* events);
CIpcClient(IEventQueue* events, int port);
CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
CIpcClient(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port);
virtual ~CIpcClient();
//! @name manipulators

View File

@ -31,17 +31,17 @@
// CIpcServer
//
CIpcServer::CIpcServer(IEventQueue* events) :
CIpcServer::CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_events(events),
m_socket(events),
m_socket(events, socketMultiplexer),
m_address(CNetworkAddress(IPC_HOST, IPC_PORT))
{
init();
}
CIpcServer::CIpcServer(IEventQueue* events, int port) :
CIpcServer::CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port) :
m_events(events),
m_socket(events),
m_socket(events, socketMultiplexer),
m_address(CNetworkAddress(IPC_HOST, port))
{
init();

View File

@ -29,6 +29,7 @@ class CEvent;
class CIpcClientProxy;
class CIpcMessage;
class IEventQueue;
class CSocketMultiplexer;
//! IPC server for communication between daemon and GUI.
/*!
@ -39,8 +40,8 @@ and allows the daemon and client/server to send log data to the GUI.
*/
class CIpcServer {
public:
CIpcServer(IEventQueue* events);
CIpcServer(IEventQueue* events, int port);
CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
CIpcServer(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, int port);
virtual ~CIpcServer();
//! @name manipulators

View File

@ -32,8 +32,6 @@
// CSocketMultiplexer
//
CSocketMultiplexer* CSocketMultiplexer::s_instance = NULL;
CSocketMultiplexer::CSocketMultiplexer() :
m_mutex(new CMutex),
m_thread(NULL),
@ -44,8 +42,6 @@ CSocketMultiplexer::CSocketMultiplexer() :
m_jobListLocker(NULL),
m_jobListLockLocker(NULL)
{
assert(s_instance == NULL);
// this pointer just has to be unique and not NULL. it will
// never be dereferenced. it's used to identify cursor nodes
// in the jobs list.
@ -54,8 +50,6 @@ CSocketMultiplexer::CSocketMultiplexer() :
// start thread
m_thread = new CThread(new TMethodJob<CSocketMultiplexer>(
this, &CSocketMultiplexer::serviceThread));
s_instance = this;
}
CSocketMultiplexer::~CSocketMultiplexer()
@ -76,15 +70,6 @@ CSocketMultiplexer::~CSocketMultiplexer()
i != m_socketJobMap.end(); ++i) {
delete *(i->second);
}
s_instance = NULL;
}
CSocketMultiplexer*
CSocketMultiplexer::getInstance()
{
assert(s_instance != NULL);
return s_instance;
}
void

View File

@ -108,8 +108,6 @@ private:
CSocketJobs m_socketJobs;
CSocketJobMap m_socketJobMap;
ISocketMultiplexerJob* m_cursorMark;
static CSocketMultiplexer* s_instance;
};
#endif

View File

@ -33,8 +33,9 @@
// CTCPListenSocket
//
CTCPListenSocket::CTCPListenSocket(IEventQueue* events) :
m_events(events)
CTCPListenSocket::CTCPListenSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_events(events),
m_socketMultiplexer(socketMultiplexer)
{
m_mutex = new CMutex;
try {
@ -49,7 +50,7 @@ CTCPListenSocket::~CTCPListenSocket()
{
try {
if (m_socket != NULL) {
CSocketMultiplexer::getInstance()->removeSocket(this);
m_socketMultiplexer->removeSocket(this);
ARCH->closeSocket(m_socket);
}
}
@ -67,7 +68,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
ARCH->setReuseAddrOnSocket(m_socket, true);
ARCH->bindSocket(m_socket, addr.getAddress());
ARCH->listenOnSocket(m_socket);
CSocketMultiplexer::getInstance()->addSocket(this,
m_socketMultiplexer->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening,
m_socket, true, false));
@ -88,7 +89,7 @@ CTCPListenSocket::close()
throw XIOClosed();
}
try {
CSocketMultiplexer::getInstance()->removeSocket(this);
m_socketMultiplexer->removeSocket(this);
ARCH->closeSocket(m_socket);
m_socket = NULL;
}
@ -108,9 +109,9 @@ CTCPListenSocket::accept()
{
IDataSocket* socket = NULL;
try {
socket = new CTCPSocket(m_events, ARCH->acceptSocket(m_socket, NULL));
socket = new CTCPSocket(m_events, m_socketMultiplexer, ARCH->acceptSocket(m_socket, NULL));
if (socket != NULL) {
CSocketMultiplexer::getInstance()->addSocket(this,
m_socketMultiplexer->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening,
m_socket, true, false));

View File

@ -25,6 +25,7 @@
class CMutex;
class ISocketMultiplexerJob;
class IEventQueue;
class CSocketMultiplexer;
//! TCP listen socket
/*!
@ -32,7 +33,7 @@ A listen socket using TCP.
*/
class CTCPListenSocket : public IListenSocket {
public:
CTCPListenSocket(IEventQueue* events);
CTCPListenSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
~CTCPListenSocket();
// ISocket overrides
@ -52,6 +53,7 @@ private:
CArchSocket m_socket;
CMutex* m_mutex;
IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
};
#endif

View File

@ -35,11 +35,12 @@
// CTCPSocket
//
CTCPSocket::CTCPSocket(IEventQueue* events) :
CTCPSocket::CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
IDataSocket(events),
m_events(events),
m_mutex(),
m_flushed(&m_mutex, true)
m_flushed(&m_mutex, true),
m_socketMultiplexer(socketMultiplexer)
{
try {
m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM);
@ -51,12 +52,13 @@ CTCPSocket::CTCPSocket(IEventQueue* events) :
init();
}
CTCPSocket::CTCPSocket(IEventQueue* events, CArchSocket socket) :
CTCPSocket::CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, CArchSocket socket) :
IDataSocket(events),
m_events(events),
m_mutex(),
m_socket(socket),
m_flushed(&m_mutex, true)
m_flushed(&m_mutex, true),
m_socketMultiplexer(socketMultiplexer)
{
assert(m_socket != NULL);
@ -316,10 +318,10 @@ CTCPSocket::setJob(ISocketMultiplexerJob* job)
{
// multiplexer will delete the old job
if (job == NULL) {
CSocketMultiplexer::getInstance()->removeSocket(this);
m_socketMultiplexer->removeSocket(this);
}
else {
CSocketMultiplexer::getInstance()->addSocket(this, job);
m_socketMultiplexer->addSocket(this, job);
}
}

View File

@ -29,6 +29,7 @@ class CMutex;
class CThread;
class ISocketMultiplexerJob;
class IEventQueue;
class CSocketMultiplexer;
//! TCP data socket
/*!
@ -36,8 +37,8 @@ A data socket using TCP.
*/
class CTCPSocket : public IDataSocket {
public:
CTCPSocket(IEventQueue* events);
CTCPSocket(IEventQueue* events, CArchSocket socket);
CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
CTCPSocket(IEventQueue* events, CSocketMultiplexer* socketMultiplexer, CArchSocket socket);
~CTCPSocket();
// ISocket overrides
@ -87,6 +88,7 @@ private:
bool m_readable;
bool m_writable;
IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
};
#endif

View File

@ -24,8 +24,9 @@
// CTCPSocketFactory
//
CTCPSocketFactory::CTCPSocketFactory(IEventQueue* events) :
m_events(events)
CTCPSocketFactory::CTCPSocketFactory(IEventQueue* events, CSocketMultiplexer* socketMultiplexer) :
m_events(events),
m_socketMultiplexer(socketMultiplexer)
{
// do nothing
}
@ -38,11 +39,11 @@ CTCPSocketFactory::~CTCPSocketFactory()
IDataSocket*
CTCPSocketFactory::create() const
{
return new CTCPSocket(m_events);
return new CTCPSocket(m_events, m_socketMultiplexer);
}
IListenSocket*
CTCPSocketFactory::createListen() const
{
return new CTCPListenSocket(m_events);
return new CTCPListenSocket(m_events, m_socketMultiplexer);
}

View File

@ -22,11 +22,12 @@
#include "ISocketFactory.h"
class IEventQueue;
class CSocketMultiplexer;
//! Socket factory for TCP sockets
class CTCPSocketFactory : public ISocketFactory {
public:
CTCPSocketFactory(IEventQueue* events);
CTCPSocketFactory(IEventQueue* events, CSocketMultiplexer* socketMultiplexer);
virtual ~CTCPSocketFactory();
// ISocketFactory overrides
@ -35,6 +36,7 @@ public:
private:
IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
};
#endif

View File

@ -83,6 +83,7 @@ public:
virtual void screensaver(bool activate) = 0;
virtual void resetOptions() = 0;
virtual void setOptions(const COptionsList& options) = 0;
virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0;
virtual CString getName() const;
private:

View File

@ -63,6 +63,9 @@ public:
*/
CClientProxy* getNextClient();
//! Get server which owns this listener
CServer* getServer() { return m_server; }
//@}
private:

View File

@ -89,6 +89,7 @@ public:
virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) = 0;
virtual void gameDeviceTimingReq() = 0;
virtual void cryptoIv(const UInt8* iv) = 0;
virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0;
private:
synergy::IStream* m_stream;

View File

@ -393,6 +393,13 @@ CClientProxy1_0::cryptoIv(const UInt8* iv)
LOG((CLOG_DEBUG "cryptoIv not supported"));
}
void
CClientProxy1_0::fileChunkSending(UInt8 mark, const UInt8* iv)
{
// ignore -- not supported in protocol 1.0
LOG((CLOG_DEBUG "fileChunkSending not supported"));
}
void
CClientProxy1_0::screensaver(bool on)
{

View File

@ -64,6 +64,7 @@ public:
virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2);
virtual void gameDeviceTimingReq();
virtual void cryptoIv(const UInt8* iv);
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
protected:
virtual bool parseHandshakeMessage(const UInt8* code);

View File

@ -0,0 +1,56 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CClientProxy1_5.h"
#include "CProtocolUtil.h"
#include "CLog.h"
#include "IStream.h"
//
// CClientProxy1_5
//
CClientProxy1_5::CClientProxy1_5(const CString& name, synergy::IStream* stream, CServer* server, IEventQueue* events) :
CClientProxy1_4(name, stream, server, events)
{
}
CClientProxy1_5::~CClientProxy1_5()
{
}
void
CClientProxy1_5::fileChunkSending(UInt8 mark, const UInt8* data)
{
CString chunk(reinterpret_cast<const char*>(data));
switch (mark) {
case '0':
LOG((CLOG_DEBUG2 "file sending start: file size = %s", data));
break;
case '1':
LOG((CLOG_DEBUG2 "file chunk sending: %s", data));
break;
case '2':
LOG((CLOG_DEBUG2 "file sending finished"));
break;
}
CProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk);
}

View File

@ -0,0 +1,32 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CClientProxy1_4.h"
class CServer;
class IEventQueue;
//! Proxy for client implementing protocol version 1.5
class CClientProxy1_5 : public CClientProxy1_4 {
public:
CClientProxy1_5(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events);
~CClientProxy1_5();
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
};

View File

@ -22,6 +22,7 @@
#include "CClientProxy1_2.h"
#include "CClientProxy1_3.h"
#include "CClientProxy1_4.h"
#include "CClientProxy1_5.h"
#include "ProtocolTypes.h"
#include "CProtocolUtil.h"
#include "XSynergy.h"
@ -221,6 +222,10 @@ CClientProxyUnknown::handleData(const CEvent&, void*)
case 4:
m_proxy = new CClientProxy1_4(name, m_stream, m_server, m_events);
break;
case 5:
m_proxy = new CClientProxy1_5(name, m_stream, m_server, m_events);
break;
}
}

View File

@ -176,6 +176,10 @@ public:
CConfig(IEventQueue* events);
virtual ~CConfig();
#ifdef TEST_ENV
CConfig() : m_inputFilter(NULL) { }
#endif
//! @name manipulators
//@{
@ -313,7 +317,8 @@ public:
Returns the hot key input filter. Clients can modify hotkeys using
that object.
*/
CInputFilter* getInputFilter();
virtual CInputFilter*
getInputFilter();
//@}
//! @name accessors
@ -339,7 +344,7 @@ public:
/*!
Returns true iff \c name names a screen.
*/
bool isScreen(const CString& name) const;
virtual bool isScreen(const CString& name) const;
//! Test for canonical screen name
/*!

View File

@ -321,6 +321,10 @@ public:
CInputFilter(const CInputFilter&);
virtual ~CInputFilter();
#ifdef TEST_ENV
CInputFilter() : m_primaryClient(NULL) { }
#endif
CInputFilter& operator=(const CInputFilter&);
// add rule, adopting the condition and the actions
@ -334,7 +338,7 @@ public:
// enable event filtering using the given primary client. disable
// if client is NULL.
void setPrimaryClient(CPrimaryClient* client);
virtual void setPrimaryClient(CPrimaryClient* client);
// convert rules to a string
CString format(const CString& linePrefix) const;

View File

@ -23,6 +23,7 @@ set(inc
CClientProxy1_2.h
CClientProxy1_3.h
CClientProxy1_4.h
CClientProxy1_5.h
CClientProxyUnknown.h
CConfig.h
CInputFilter.h
@ -39,6 +40,7 @@ set(src
CClientProxy1_2.cpp
CClientProxy1_3.cpp
CClientProxy1_4.cpp
CClientProxy1_5.cpp
CClientProxyUnknown.cpp
CConfig.cpp
CInputFilter.cpp

View File

@ -28,7 +28,8 @@
CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) :
CBaseClientProxy(name),
m_screen(screen),
m_fakeInputCount(0)
m_fakeInputCount(0),
m_mock(false)
{
// all clipboards are clean
for (UInt32 i = 0; i < kClipboardEnd; ++i) {
@ -272,6 +273,12 @@ CPrimaryClient::screensaver(bool)
// ignore
}
void
CPrimaryClient::fileChunkSending(UInt8 mark, const UInt8* data)
{
// ignore
}
void
CPrimaryClient::resetOptions()
{

View File

@ -38,6 +38,10 @@ public:
CPrimaryClient(const CString& name, CScreen* screen);
~CPrimaryClient();
#ifdef TEST_ENV
CPrimaryClient() : CBaseClientProxy(""), m_mock(true) { }
#endif
//! @name manipulators
//@{
@ -45,20 +49,20 @@ public:
/*!
Handles reconfiguration of jump zones.
*/
void reconfigure(UInt32 activeSides);
virtual void reconfigure(UInt32 activeSides);
//! Register a system hotkey
/*!
Registers a system-wide hotkey for key \p key with modifiers \p mask.
Returns an id used to unregister the hotkey.
*/
UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
//! Unregister a system hotkey
/*!
Unregisters a previously registered hot key.
*/
void unregisterHotKey(UInt32 id);
virtual void unregisterHotKey(UInt32 id);
//! Prepare to synthesize input on primary screen
/*!
@ -98,7 +102,8 @@ public:
/*!
Returns the primary screen's current toggle modifier key state.
*/
KeyModifierMask getToggleMask() const;
virtual KeyModifierMask
getToggleMask() const;
//! Get screen lock state
/*!
@ -143,11 +148,13 @@ public:
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
private:
CScreen* m_screen;
bool m_clipboardDirty[kClipboardEnd];
SInt32 m_fakeInputCount;
bool m_mock;
};
#endif

View File

@ -41,7 +41,7 @@
// CServer
//
CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events) :
CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events) :
m_events(events),
m_mock(false),
m_primaryClient(primaryClient),
@ -51,8 +51,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen*
m_yDelta(0),
m_xDelta2(0),
m_yDelta2(0),
m_config(events),
m_inputFilter(m_config.getInputFilter()),
m_config(&config),
m_inputFilter(config.getInputFilter()),
m_activeSaver(NULL),
m_switchDir(kNoDirection),
m_switchScreen(NULL),
@ -173,6 +173,10 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen*
m_inputFilter,
new TMethodEventJob<CServer>(this,
&CServer::handleFakeInputEndEvent));
m_events->adoptHandler(m_events->forIScreen().fileChunkSending(),
this,
new TMethodEventJob<CServer>(this,
&CServer::handleFileChunkSendingEvent));
// add connection
addClient(m_primaryClient);
@ -259,7 +263,6 @@ CServer::setConfig(const CConfig& config)
closeClients(config);
// cut over
m_config = config;
processOptions();
// add ScrollLock as a hotkey to lock to the screen. this was a
@ -269,7 +272,7 @@ CServer::setConfig(const CConfig& config)
// we will unfortunately generate a warning. if the user has
// configured a CLockCursorToScreenAction then we don't add
// ScrollLock as a hotkey.
if (!m_config.hasLockToScreenAction()) {
if (!m_config->hasLockToScreenAction()) {
IPlatformScreen::CKeyInfo* key =
IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0);
CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(m_events, key));
@ -301,7 +304,7 @@ CServer::adoptClient(CBaseClientProxy* client)
&CServer::handleClientDisconnected, client));
// name must be in our configuration
if (!m_config.isScreen(client->getName())) {
if (!m_config->isScreen(client->getName())) {
LOG((CLOG_WARN "unrecognised client name \"%s\", check server config", client->getName().c_str()));
closeClient(client, kMsgEUnknown);
return;
@ -375,7 +378,7 @@ CServer::getClients(std::vector<CString>& list) const
CString
CServer::getName(const CBaseClientProxy* client) const
{
CString name = m_config.getCanonicalName(client->getName());
CString name = m_config->getCanonicalName(client->getName());
if (name.empty()) {
name = client->getName();
}
@ -579,7 +582,7 @@ CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const
{
assert(client != NULL);
return m_config.hasNeighbor(getName(client), dir);
return m_config->hasNeighbor(getName(client), dir);
}
CBaseClientProxy*
@ -601,7 +604,7 @@ CServer::getNeighbor(CBaseClientProxy* src,
// search for the closest neighbor that exists in direction dir
float tTmp;
for (;;) {
CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp));
CString dstName(m_config->getNeighbor(srcName, dir, t, &tTmp));
// if nothing in that direction then return NULL. if the
// destination is the source then we can make no more
@ -757,25 +760,25 @@ CServer::avoidJumpZone(CBaseClientProxy* dst,
// don't need to move inwards because that side can't provoke a jump.
switch (dir) {
case kLeft:
if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() &&
if (!m_config->getNeighbor(dstName, kRight, t, NULL).empty() &&
x > dx + dw - 1 - z)
x = dx + dw - 1 - z;
break;
case kRight:
if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() &&
if (!m_config->getNeighbor(dstName, kLeft, t, NULL).empty() &&
x < dx + z)
x = dx + z;
break;
case kTop:
if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() &&
if (!m_config->getNeighbor(dstName, kBottom, t, NULL).empty() &&
y > dy + dh - 1 - z)
y = dy + dh - 1 - z;
break;
case kBottom:
if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() &&
if (!m_config->getNeighbor(dstName, kTop, t, NULL).empty() &&
y < dy + z)
y = dy + z;
break;
@ -839,9 +842,9 @@ CServer::isSwitchOkay(CBaseClientProxy* newScreen,
// are we in a locked corner? first check if screen has the option set
// and, if not, check the global options.
const CConfig::CScreenOptions* options =
m_config.getOptions(getName(m_active));
m_config->getOptions(getName(m_active));
if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) {
options = m_config.getOptions("");
options = m_config->getOptions("");
}
if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) {
// get corner mask and size
@ -1089,7 +1092,7 @@ CServer::sendOptions(CBaseClientProxy* client) const
// look up options for client
const CConfig::CScreenOptions* options =
m_config.getOptions(getName(client));
m_config->getOptions(getName(client));
if (options != NULL) {
// convert options to a more convenient form for sending
optionsList.reserve(2 * options->size());
@ -1101,7 +1104,7 @@ CServer::sendOptions(CBaseClientProxy* client) const
}
// look up global options
options = m_config.getOptions("");
options = m_config->getOptions("");
if (options != NULL) {
// convert options to a more convenient form for sending
optionsList.reserve(optionsList.size() + 2 * options->size());
@ -1120,7 +1123,7 @@ CServer::sendOptions(CBaseClientProxy* client) const
void
CServer::processOptions()
{
const CConfig::CScreenOptions* options = m_config.getOptions("");
const CConfig::CScreenOptions* options = m_config->getOptions("");
if (options == NULL) {
return;
}
@ -1510,6 +1513,13 @@ CServer::handleFakeInputEndEvent(const CEvent&, void*)
m_primaryClient->fakeInputEnd();
}
void
CServer::handleFileChunkSendingEvent(const CEvent& event, void*)
{
UInt8* data = reinterpret_cast<UInt8*>(event.getData());
onFileChunkSending(data);
}
void
CServer::onClipboardChanged(CBaseClientProxy* sender,
ClipboardID id, UInt32 seqNum)
@ -1970,6 +1980,16 @@ CServer::onGameDeviceTimingReq()
m_active->gameDeviceTimingReq();
}
void
CServer::onFileChunkSending(const UInt8* data)
{
LOG((CLOG_DEBUG1 "onFileChunkSending"));
assert(m_active != NULL);
// relay
m_active->fileChunkSending(data[0], &data[1]);
}
bool
CServer::addClient(CBaseClientProxy* client)
{

View File

@ -103,11 +103,12 @@ public:
client (local screen) \p primaryClient. The client retains
ownership of \p primaryClient.
*/
CServer(const CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events);
CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events);
~CServer();
#ifdef TEST_ENV
CServer() : m_mock(true), m_events(NULL), m_config(NULL) { }
CServer() : m_mock(true), m_config(NULL) { }
void setActive(CBaseClientProxy* active) { m_active = active; }
#endif
//! @name manipulators
@ -297,6 +298,7 @@ private:
void handleLockCursorToScreenEvent(const CEvent&, void*);
void handleFakeInputBeginEvent(const CEvent&, void*);
void handleFakeInputEndEvent(const CEvent&, void*);
void handleFileChunkSendingEvent(const CEvent&, void*);
// event processing
void onClipboardChanged(CBaseClientProxy* sender,
@ -316,6 +318,7 @@ private:
void onGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2);
void onGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2);
void onGameDeviceTimingReq();
void onFileChunkSending(const UInt8* data);
// add client to list and attach event handlers for client
bool addClient(CBaseClientProxy*);
@ -386,7 +389,7 @@ private:
SInt32 m_xDelta2, m_yDelta2;
// current configuration
CConfig m_config;
CConfig* m_config;
// input filter (from m_config);
CInputFilter* m_inputFilter;

View File

@ -357,7 +357,7 @@ CApp::initApp(int argc, const char** argv)
void
CApp::initIpcClient()
{
m_ipcClient = new CIpcClient(m_events);
m_ipcClient = new CIpcClient(m_events, m_socketMultiplexer);
m_ipcClient->connect();
m_events->adoptHandler(

View File

@ -35,6 +35,7 @@ class ILogOutputter;
class CFileLogOutputter;
class CScreen;
class IEventQueue;
class CSocketMultiplexer;
typedef IArchTaskBarReceiver* (*CreateTaskBarReceiverFunc)(const CBufferedLogOutputter*, IEventQueue* events);
@ -97,6 +98,9 @@ public:
virtual IEventQueue* getEvents() const { return m_events; }
void setSocketMultiplexer(CSocketMultiplexer* sm) { m_socketMultiplexer = sm; }
CSocketMultiplexer* getSocketMultiplexer() const { return m_socketMultiplexer; }
private:
void handleIpcMessage(const CEvent&, void*);
@ -117,6 +121,7 @@ private:
ARCH_APP_UTIL m_appUtil;
CIpcClient* m_ipcClient;
IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
};
#define BYE "\nTry `%s --help' for more information."

View File

@ -391,7 +391,13 @@ CClient*
CClientApp::openClient(const CString& name, const CNetworkAddress& address, CScreen* screen, const CCryptoOptions& crypto)
{
CClient* client = new CClient(
m_events, name, address, new CTCPSocketFactory(m_events), NULL, screen, crypto);
m_events,
name,
address,
new CTCPSocketFactory(m_events, getSocketMultiplexer()),
NULL,
screen,
crypto);
try {
m_events->adoptHandler(
@ -522,6 +528,7 @@ CClientApp::mainLoop()
// create socket multiplexer. this must happen after daemonization
// on unix because threads evaporate across a fork().
CSocketMultiplexer multiplexer;
setSocketMultiplexer(&multiplexer);
// start client, etc
appUtil().startNode();

View File

@ -203,7 +203,7 @@ CDaemonApp::mainLoop(bool logToFile)
CSocketMultiplexer multiplexer;
// uses event queue, must be created here.
m_ipcServer = new CIpcServer(m_events);
m_ipcServer = new CIpcServer(m_events, &multiplexer);
// send logging to gui via ipc, log system adopts outputter.
m_ipcLogOutputter = new CIpcLogOutputter(*m_ipcServer);

View File

@ -35,7 +35,8 @@ CScreen::CScreen(IPlatformScreen* platformScreen, IEventQueue* events) :
m_enabled(false),
m_entered(m_isPrimary),
m_screenSaverSync(true),
m_fakeInput(false)
m_fakeInput(false),
m_mock(false)
{
assert(m_screen != NULL);
@ -47,6 +48,10 @@ CScreen::CScreen(IPlatformScreen* platformScreen, IEventQueue* events) :
CScreen::~CScreen()
{
if (m_mock) {
return;
}
if (m_enabled) {
disable();
}

View File

@ -40,6 +40,10 @@ public:
CScreen(IPlatformScreen* platformScreen, IEventQueue* events);
virtual ~CScreen();
#ifdef TEST_ENV
CScreen() : m_mock(true) { }
#endif
//! @name manipulators
//@{
@ -49,14 +53,14 @@ public:
For a secondary screen it also means disabling the screen saver if
synchronizing it and preparing to synthesize events.
*/
void enable();
virtual void enable();
//! Deactivate screen
/*!
Undoes the operations in activate() and events are no longer
reported. It also releases keys that are logically pressed.
*/
void disable();
virtual void disable();
//! Enter screen
/*!
@ -208,14 +212,14 @@ public:
/*!
Resets all options to their default values.
*/
void resetOptions();
virtual void resetOptions();
//! Notify of options changes
/*!
Set options to given values. Ignores unknown options and doesn't
modify options that aren't given in \c options.
*/
void setOptions(const COptionsList& options);
virtual void setOptions(const COptionsList& options);
//! Set clipboard sequence number
/*!
@ -343,6 +347,8 @@ private:
bool m_fakeInput;
IEventQueue* m_events;
bool m_mock;
};
#endif

View File

@ -670,8 +670,11 @@ CClientListener*
CServerApp::openClientListener(const CNetworkAddress& address)
{
CClientListener* listen = new CClientListener(
address, new CTCPSocketFactory(m_events),
NULL, args().m_crypto, m_events);
address,
new CTCPSocketFactory(m_events, getSocketMultiplexer()),
NULL,
args().m_crypto,
m_events);
m_events->adoptHandler(
m_events->forCClientListener().connected(), listen,
@ -682,7 +685,7 @@ CServerApp::openClientListener(const CNetworkAddress& address)
}
CServer*
CServerApp::openServer(const CConfig& config, CPrimaryClient* primaryClient)
CServerApp::openServer(CConfig& config, CPrimaryClient* primaryClient)
{
CServer* server = new CServer(config, primaryClient, s_serverScreen, m_events);
@ -720,6 +723,7 @@ CServerApp::mainLoop()
// create socket multiplexer. this must happen after daemonization
// on unix because threads evaporate across a fork().
CSocketMultiplexer multiplexer;
setSocketMultiplexer(&multiplexer);
// if configuration has no screens then add this system
// as the default

View File

@ -100,7 +100,7 @@ public:
void handleSuspend(const CEvent&, void*);
void handleResume(const CEvent&, void*);
CClientListener* openClientListener(const CNetworkAddress& address);
CServer* openServer(const CConfig& config, CPrimaryClient* primaryClient);
CServer* openServer(CConfig& config, CPrimaryClient* primaryClient);
void handleNoClients(const CEvent&, void*);
bool startServer();
int mainLoop();

View File

@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IInterface.h"
class INode : IInterface {

View File

@ -51,6 +51,7 @@ const char* kMsgDGameSticks = "DGST%1i%2i%2i%2i%2i";
const char* kMsgDGameTriggers = "DGTR%1i%1i%1i";
const char* kMsgDGameFeedback = "DGFB%1i%2i%2i";
const char* kMsgDCryptoIv = "DCIV%s";
const char* kMsgDFileTransfer = "DFTR%1i%s";
const char* kMsgQInfo = "QINF";
const char* kMsgEIncompatible = "EICV%2i%2i";
const char* kMsgEBusy = "EBSY";

View File

@ -29,7 +29,7 @@
// adds horizontal mouse scrolling
// 1.4: adds game device support
static const SInt16 kProtocolMajorVersion = 1;
static const SInt16 kProtocolMinorVersion = 4;
static const SInt16 kProtocolMinorVersion = 5;
// default contact port number
static const UInt16 kDefaultPort = 24800;
@ -288,6 +288,13 @@ extern const char* kMsgDSetOptions;
// cryptography stream.
extern const char* kMsgDCryptoIv;
// file data: primary <-> secondary
// transfer file data. A mark is used in the first byte.
// 0 means the content followed is the file size.
// 1 means the content followed is the chunk data.
// 2 means the file transfer is finished.
extern const char* kMsgDFileTransfer;
//
// query codes
//

View File

@ -35,7 +35,7 @@
#include "CString.h"
#include "CIpcServerProxy.h"
#include "CIpcMessage.h"
#include "CSimpleEventQueueBuffer.h"
#include "CTestEventQueue.h"
#define TEST_IPC_PORT 24802
@ -49,18 +49,9 @@ public:
void sendMessageToServer_serverHandleMessageReceived(const CEvent&, void*);
void sendMessageToClient_serverHandleClientConnected(const CEvent&, void*);
void sendMessageToClient_clientHandleMessageReceived(const CEvent&, void*);
void handleQuitTimeout(const CEvent&, void* vclient);
void raiseQuitEvent();
void initQuitTimeout(double timeout);
void cleanupQuitTimeout();
private:
void timeoutThread(void*);
public:
CSocketMultiplexer m_multiplexer;
CEventQueue m_events;
CEventQueueTimer* m_quitTimeoutTimer;
bool m_connectToServer_helloMessageReceived;
bool m_connectToServer_hasClientNode;
CIpcServer* m_connectToServer_server;
@ -68,12 +59,14 @@ public:
CString m_sendMessageToClient_receivedString;
CIpcClient* m_sendMessageToServer_client;
CIpcServer* m_sendMessageToClient_server;
CTestEventQueue m_events;
};
TEST_F(CIpcTests, connectToServer)
{
CIpcServer server(&m_events, TEST_IPC_PORT);
CSocketMultiplexer socketMultiplexer;
CIpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
server.listen();
m_connectToServer_server = &server;
@ -82,13 +75,13 @@ TEST_F(CIpcTests, connectToServer)
new TMethodEventJob<CIpcTests>(
this, &CIpcTests::connectToServer_handleMessageReceived));
CIpcClient client(&m_events, TEST_IPC_PORT);
CIpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
client.connect();
initQuitTimeout(5);
m_events.initQuitTimeout(5);
m_events.loop();
m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server);
cleanupQuitTimeout();
m_events.cleanupQuitTimeout();
EXPECT_EQ(true, m_connectToServer_helloMessageReceived);
EXPECT_EQ(true, m_connectToServer_hasClientNode);
@ -96,7 +89,8 @@ TEST_F(CIpcTests, connectToServer)
TEST_F(CIpcTests, sendMessageToServer)
{
CIpcServer server(&m_events, TEST_IPC_PORT);
CSocketMultiplexer socketMultiplexer;
CIpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
server.listen();
// event handler sends "test" command to server.
@ -105,21 +99,22 @@ TEST_F(CIpcTests, sendMessageToServer)
new TMethodEventJob<CIpcTests>(
this, &CIpcTests::sendMessageToServer_serverHandleMessageReceived));
CIpcClient client(&m_events, TEST_IPC_PORT);
CIpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
client.connect();
m_sendMessageToServer_client = &client;
initQuitTimeout(5);
m_events.initQuitTimeout(5);
m_events.loop();
m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server);
cleanupQuitTimeout();
m_events.cleanupQuitTimeout();
EXPECT_EQ("test", m_sendMessageToServer_receivedString);
}
TEST_F(CIpcTests, sendMessageToClient)
{
CIpcServer server(&m_events, TEST_IPC_PORT);
CSocketMultiplexer socketMultiplexer;
CIpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
server.listen();
m_sendMessageToClient_server = &server;
@ -129,7 +124,7 @@ TEST_F(CIpcTests, sendMessageToClient)
new TMethodEventJob<CIpcTests>(
this, &CIpcTests::sendMessageToClient_serverHandleClientConnected));
CIpcClient client(&m_events, TEST_IPC_PORT);
CIpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
client.connect();
m_events.adoptHandler(
@ -137,17 +132,16 @@ TEST_F(CIpcTests, sendMessageToClient)
new TMethodEventJob<CIpcTests>(
this, &CIpcTests::sendMessageToClient_clientHandleMessageReceived));
initQuitTimeout(5);
m_events.initQuitTimeout(5);
m_events.loop();
m_events.removeHandler(m_events.forCIpcServer().messageReceived(), &server);
m_events.removeHandler(m_events.forCIpcClient().messageReceived(), &client);
cleanupQuitTimeout();
m_events.cleanupQuitTimeout();
EXPECT_EQ("test", m_sendMessageToClient_receivedString);
}
CIpcTests::CIpcTests() :
m_quitTimeoutTimer(nullptr),
m_connectToServer_helloMessageReceived(false),
m_connectToServer_hasClientNode(false),
m_connectToServer_server(nullptr),
@ -168,7 +162,7 @@ CIpcTests::connectToServer_handleMessageReceived(const CEvent& e, void*)
m_connectToServer_hasClientNode =
m_connectToServer_server->hasClients(kIpcClientNode);
m_connectToServer_helloMessageReceived = true;
raiseQuitEvent();
m_events.raiseQuitEvent();
}
}
@ -185,7 +179,7 @@ CIpcTests::sendMessageToServer_serverHandleMessageReceived(const CEvent& e, void
CIpcCommandMessage* cm = static_cast<CIpcCommandMessage*>(m);
LOG((CLOG_DEBUG "got ipc command message, %d", cm->command().c_str()));
m_sendMessageToServer_receivedString = cm->command();
raiseQuitEvent();
m_events.raiseQuitEvent();
}
}
@ -208,37 +202,6 @@ CIpcTests::sendMessageToClient_clientHandleMessageReceived(const CEvent& e, void
CIpcLogLineMessage* llm = static_cast<CIpcLogLineMessage*>(m);
LOG((CLOG_DEBUG "got ipc log message, %d", llm->logLine().c_str()));
m_sendMessageToClient_receivedString = llm->logLine();
raiseQuitEvent();
m_events.raiseQuitEvent();
}
}
void
CIpcTests::raiseQuitEvent()
{
m_events.addEvent(CEvent(CEvent::kQuit));
}
void
CIpcTests::initQuitTimeout(double timeout)
{
assert(m_quitTimeoutTimer == nullptr);
m_quitTimeoutTimer = m_events.newOneShotTimer(timeout, NULL);
m_events.adoptHandler(CEvent::kTimer, m_quitTimeoutTimer,
new TMethodEventJob<CIpcTests>(
this, &CIpcTests::handleQuitTimeout));
}
void
CIpcTests::cleanupQuitTimeout()
{
m_events.removeHandler(CEvent::kTimer, m_quitTimeoutTimer);
delete m_quitTimeoutTimer;
m_quitTimeoutTimer = nullptr;
}
void
CIpcTests::handleQuitTimeout(const CEvent&, void* vclient)
{
LOG((CLOG_ERR "timeout"));
raiseQuitEvent();
}

View File

@ -14,9 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(h
CTestEventQueue.h
)
set(src
${h}
Main.cpp
CIpcTests.cpp
NetworkTests.cpp
CTestEventQueue.cpp
)
if (WIN32)
@ -50,11 +57,11 @@ set(inc
../../lib/mt
../../lib/net
../../lib/platform
../../lib/server
../../lib/synergy
../../../tools/gtest-1.6.0/include
../../../tools/gmock-1.6.0/include
../unittests
../unittests/synergy
)
if (UNIX)
@ -72,4 +79,4 @@ endif()
include_directories(${inc})
add_executable(integtests ${src})
target_link_libraries(integtests
arch base client common io ipc mt net platform server synergy gtest gmock ${libs})
arch base client common io ipc mt net platform server synergy gtest gmock cryptopp ${libs})

View File

@ -0,0 +1,52 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CTestEventQueue.h"
#include "CLog.h"
#include "TMethodEventJob.h"
#include "CSimpleEventQueueBuffer.h"
void
CTestEventQueue::raiseQuitEvent()
{
addEvent(CEvent(CEvent::kQuit));
}
void
CTestEventQueue::initQuitTimeout(double timeout)
{
assert(m_quitTimeoutTimer == nullptr);
m_quitTimeoutTimer = newOneShotTimer(timeout, NULL);
adoptHandler(CEvent::kTimer, m_quitTimeoutTimer,
new TMethodEventJob<CTestEventQueue>(
this, &CTestEventQueue::handleQuitTimeout));
}
void
CTestEventQueue::cleanupQuitTimeout()
{
removeHandler(CEvent::kTimer, m_quitTimeoutTimer);
delete m_quitTimeoutTimer;
m_quitTimeoutTimer = nullptr;
}
void
CTestEventQueue::handleQuitTimeout(const CEvent&, void* vclient)
{
LOG((CLOG_ERR "timeout"));
raiseQuitEvent();
}

View File

@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CEventQueue.h"
class CEventQueueTimer;
class CTestEventQueue : public CEventQueue {
public:
CTestEventQueue() : m_quitTimeoutTimer(nullptr) { }
void handleQuitTimeout(const CEvent&, void* vclient);
void raiseQuitEvent();
void initQuitTimeout(double timeout);
void cleanupQuitTimeout();
private:
void timeoutThread(void*);
private:
CEventQueueTimer* m_quitTimeoutTimer;
};

View File

@ -0,0 +1,188 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include <iostream>
#include <fstream>
#define TEST_ENV
#include "CLog.h"
#include "CServer.h"
#include "CClient.h"
#include "TMethodEventJob.h"
#include "server/CMockConfig.h"
#include "server/CMockPrimaryClient.h"
#include "synergy/CMockScreen.h"
#include "CClientListener.h"
#include "CNetworkAddress.h"
#include "CTCPSocketFactory.h"
#include "CCryptoOptions.h"
#include "CSocketMultiplexer.h"
#include "CMSWindowsScreen.h"
#include "CGameDevice.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CTestEventQueue.h"
#include "server/CMockInputFilter.h"
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Invoke;
#define TEST_PORT 24803
#define TEST_HOST "localhost"
const int klargeDataSize = 512;
char g_largeData[klargeDataSize] = "large data:head.1221412312341244213123fdsfasdawdwadwadacwdd.12321412312341244213123fdsfasdawdwadwadacwdawddawdwacawdawd232141231awddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewedacwdawddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewe12134123njk1u31i2nm3e123hu23oi132213njk.tail";
void sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
void sendFileToClient_getCursorPos(SInt32& x, SInt32& y);
class NetworkTests : public ::testing::Test
{
public:
NetworkTests() { }
void sendData(CServer* server);
void sendFileToClient_handleClientConnected(const CEvent&, void* vlistener);
void sendFileToClient_fileRecieveComplete(const CEvent&, void*);
public:
CTestEventQueue m_events;
};
TEST_F(NetworkTests, sendFileToClient)
{
// server and client
CNetworkAddress serverAddress(TEST_HOST, TEST_PORT);
CCryptoOptions cryptoOptions;
serverAddress.resolve();
// server
CSocketMultiplexer serverSocketMultiplexer;
CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer);
CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events);
NiceMock<CMockScreen> serverScreen;
NiceMock<CMockPrimaryClient> primaryClient;
NiceMock<CMockConfig> serverConfig;
NiceMock<CMockInputFilter> serverInputFilter;
m_events.adoptHandler(
m_events.forCClientListener().connected(), &listener,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendFileToClient_handleClientConnected, &listener));
ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
CServer server(serverConfig, &primaryClient, &serverScreen, &m_events);
server.m_mock = true;
listener.setServer(&server);
// client
NiceMock<CMockScreen> clientScreen;
CSocketMultiplexer clientSocketMultiplexer;
CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer);
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(sendFileToClient_getShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(sendFileToClient_getCursorPos));
CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions);
m_events.adoptHandler(
m_events.forIScreen().fileRecieveComplete(), &client,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendFileToClient_fileRecieveComplete));
client.connect();
m_events.initQuitTimeout(10);
m_events.loop();
m_events.cleanupQuitTimeout();
}
void
NetworkTests::sendFileToClient_handleClientConnected(const CEvent&, void* vlistener)
{
CClientListener* listener = reinterpret_cast<CClientListener*>(vlistener);
CServer* server = listener->getServer();
CClientProxy* client = listener->getNextClient();
if (client == NULL) {
throw std::exception("client is null");
}
CBaseClientProxy* bcp = reinterpret_cast<CBaseClientProxy*>(client);
server->adoptClient(bcp);
server->setActive(bcp);
sendData(server);
}
void
NetworkTests::sendFileToClient_fileRecieveComplete(const CEvent& event, void*)
{
CClient* client = reinterpret_cast<CClient*>(event.getTarget());
EXPECT_TRUE(client->isReceivedFileSizeValid());
m_events.raiseQuitEvent();
}
void
NetworkTests::sendData(CServer* server)
{
UInt8* largeDataSize = new UInt8[5];
largeDataSize[0] = '0';
largeDataSize[1] = '5';
largeDataSize[2] = '1';
largeDataSize[3] = '1';
largeDataSize[4] = '\0';
// transfer data from server -> client
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, largeDataSize));
UInt8* largeData = new UInt8[klargeDataSize + 1];
largeData[0] = '1';
memcpy(&largeData[1], g_largeData, klargeDataSize);
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, (UInt8*)largeData));
UInt8* transferFinished = new UInt8[2];
transferFinished[0] = '2';
transferFinished[1] = '\0';
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, transferFinished));
}
void
sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h)
{
x = 0;
y = 0;
w = 1;
h = 1;
}
void
sendFileToClient_getCursorPos(SInt32& x, SInt32& y)
{
x = 0;
y = 0;
}

View File

@ -27,8 +27,8 @@
#include "CMSWindowsScreen.h"
#include "CMSWindowsScreenSaver.h"
#include "TMethodJob.h"
#include "CMockEventQueue.h"
#include "CMockKeyMap.h"
#include "synergy/CMockEventQueue.h"
#include "synergy/CMockKeyMap.h"
// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4

View File

@ -22,6 +22,10 @@ set(h
io/CMockStream.h
server/CMockServer.h
io/CMockCryptoStream.h
synergy/CMockScreen.h
server/CMockConfig.h
server/CMockPrimaryClient.h
server/CMockInputFilter.h
)
set(src

View File

@ -26,7 +26,6 @@ class CMockCryptoStream : public CCryptoStream
public:
CMockCryptoStream(IEventQueue* eventQueue, IStream* stream) :
CCryptoStream(eventQueue, stream, CCryptoOptions("gcm", "stub"), false) { }
MOCK_METHOD2(read, UInt32(void*, UInt32));
MOCK_METHOD2(write, void(const void*, UInt32));
};

View File

@ -0,0 +1,31 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CConfig.h"
class CMockConfig : public CConfig
{
public:
CMockConfig() : CConfig() { }
MOCK_METHOD0(getInputFilter, CInputFilter*());
MOCK_CONST_METHOD1(isScreen, bool(const CString&));
};

View File

@ -0,0 +1,29 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CInputFilter.h"
class CMockInputFilter : public CInputFilter
{
public:
MOCK_METHOD1(setPrimaryClient, void(CPrimaryClient*));
};

View File

@ -0,0 +1,40 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CPrimaryClient.h"
#include "CString.h"
class CMockPrimaryClient : public CPrimaryClient
{
public:
MOCK_CONST_METHOD0(getEventTarget, void*());
MOCK_CONST_METHOD2(getCursorPos, void(SInt32&, SInt32&));
MOCK_CONST_METHOD2(setJumpCursorPos, void(SInt32, SInt32));
MOCK_METHOD1(reconfigure, void(UInt32));
MOCK_METHOD0(resetOptions, void());
MOCK_METHOD1(setOptions, void(const COptionsList&));
MOCK_METHOD0(enable, void());
MOCK_METHOD0(disable, void());
MOCK_METHOD2(registerHotKey, UInt32(KeyID, KeyModifierMask));
MOCK_CONST_METHOD0(getToggleMask, KeyModifierMask());
MOCK_METHOD1(unregisterHotKey, void(UInt32));
};

View File

@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gmock/gmock.h>
#define TEST_ENV
#include "CScreen.h"
class CMockScreen : public CScreen
{
public:
CMockScreen() : CScreen() { }
MOCK_METHOD0(disable, void());
MOCK_CONST_METHOD4(getShape, void(SInt32&, SInt32&, SInt32&, SInt32&));
MOCK_CONST_METHOD2(getCursorPos, void(SInt32&, SInt32&));
MOCK_METHOD0(resetOptions, void());
MOCK_METHOD1(setOptions, void(const COptionsList&));
MOCK_METHOD0(enable, void());
};