Changes: Connection unit tests refactored, test mode added to

Connection, comments added / improved.

Details: Avoid creating useless sockets in the unit tests by using a
         dedicated test mode.
pull/1/head
Jussi Lind 15 years ago
parent 2959fb7b47
commit 07207ee76a

@ -34,7 +34,8 @@
PoolType Connection::socketPool; PoolType Connection::socketPool;
Connection::Connection(const string socketId) : Connection::Connection(const string socketId, bool testMode) :
m_testMode(testMode),
m_fd(-1), m_fd(-1),
m_curSocket(findSocket(socketId)), m_curSocket(findSocket(socketId)),
m_fileName(""), m_fileName(""),
@ -47,9 +48,9 @@ Connection::Connection(const string socketId) :
m_io[1] = -1; m_io[1] = -1;
m_io[2] = -1; m_io[2] = -1;
if (m_curSocket == -1) if (!m_testMode && m_curSocket == -1)
{ {
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: socket isn't initialized\n"); Logger::logErrorAndDie(EXIT_FAILURE, "Connection: Socket isn't initialized!\n");
} }
#if defined (HAVE_CREDS) && ! defined (DISABLE_VERIFICATION) #if defined (HAVE_CREDS) && ! defined (DISABLE_VERIFICATION)
@ -64,13 +65,9 @@ Connection::Connection(const string socketId) :
#endif #endif
} }
Connection::~Connection()
{}
void Connection::closeAllSockets() void Connection::closeAllSockets()
{ {
PoolType::iterator it; PoolType::iterator it;
for (it = socketPool.begin(); it != socketPool.end(); ++it) for (it = socketPool.begin(); it != socketPool.end(); ++it)
{ {
if (it->second > 0) if (it->second > 0)
@ -89,71 +86,83 @@ int Connection::findSocket(const string socketId)
void Connection::initSocket(const string socketId) void Connection::initSocket(const string socketId)
{ {
// Initialize a socket at socketId if one already doesn't
// exist for that id / path.
PoolType::iterator it(socketPool.find(socketId)); PoolType::iterator it(socketPool.find(socketId));
if (it == socketPool.end()) if (it == socketPool.end())
{ {
Logger::logInfo("%s: init socket '%s'", __FUNCTION__, socketId.c_str()); Logger::logInfo("Initing socket at '%s'..", socketId.c_str());
int sockfd = socket(PF_UNIX, SOCK_STREAM, 0); // Create a new local socket
if (sockfd < 0) int socketFd = socket(PF_UNIX, SOCK_STREAM, 0);
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: opening invoker socket\n"); if (socketFd < 0)
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: Failed to open socket\n");
// Remove the previous socket file
unlink(socketId.c_str()); unlink(socketId.c_str());
// Initialize the socket struct
struct sockaddr sun; struct sockaddr sun;
sun.sa_family = AF_UNIX; sun.sa_family = AF_UNIX;
int maxLen = sizeof(sun.sa_data) - 1; int maxLen = sizeof(sun.sa_data) - 1;
strncpy(sun.sa_data, socketId.c_str(), maxLen); strncpy(sun.sa_data, socketId.c_str(), maxLen);
sun.sa_data[maxLen] = '\0'; sun.sa_data[maxLen] = '\0';
if (bind(sockfd, &sun, sizeof(sun)) < 0) // Bind the socket
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: binding to invoker socket\n"); if (bind(socketFd, &sun, sizeof(sun)) < 0)
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: Failed to bind to socket (fd=%d)\n", socketFd);
if (listen(sockfd, 10) < 0) // Listen to the socket
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: listening to invoker socket\n"); if (listen(socketFd, 10) < 0)
Logger::logErrorAndDie(EXIT_FAILURE, "Connection: Failed to listen to socket (fd=%d)\n", socketFd);
// Set permissions
chmod(socketId.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | chmod(socketId.c_str(), S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IWGRP | S_IXGRP | S_IRGRP | S_IWGRP | S_IXGRP |
S_IROTH | S_IWOTH | S_IXOTH); S_IROTH | S_IWOTH | S_IXOTH);
socketPool[socketId] = sockfd; // Store path <-> file descriptor mapping
socketPool[socketId] = socketFd;
} }
} }
bool Connection::acceptConn(AppData & rApp) bool Connection::acceptConn(AppData & rApp)
{ {
m_fd = accept(m_curSocket, NULL, NULL); if (!m_testMode)
if (m_fd < 0)
{ {
Logger::logError("Connection: accepting connections (%s)\n", strerror(errno)); m_fd = accept(m_curSocket, NULL, NULL);
return false;
} if (m_fd < 0)
{
Logger::logError("Connection: Failed to accept a connection: %s\n", strerror(errno));
return false;
}
#if defined (HAVE_CREDS) #if defined (HAVE_CREDS)
// Get credentials of assumed invoker // Get credentials of assumed invoker
creds_t ccreds = creds_getpeer(m_fd); creds_t ccreds = creds_getpeer(m_fd);
// Fetched peer creds will be free'd with rApp.deletePeerCreds // Fetched peer creds will be free'd with rApp.deletePeerCreds
rApp.setPeerCreds(ccreds); rApp.setPeerCreds(ccreds);
#if ! defined (DISABLE_VERIFICATION) #if ! defined (DISABLE_VERIFICATION)
// This code checks if the assumed invoker has got enough // This code checks if the assumed invoker has got enough
// rights to communicate with us // rights to communicate with us
if (!creds_have_p(ccreds, m_credsType, m_credsValue)) if (!creds_have_p(ccreds, m_credsType, m_credsValue))
{ {
Logger::logError("Connection: invoker doesn't have enough credentials to call launcher \n"); Logger::logError("Connection: invoker doesn't have enough credentials to call launcher \n");
sendMsg(INVOKER_MSG_BAD_CREDS); sendMsg(INVOKER_MSG_BAD_CREDS);
closeConn(); closeConn();
return false; return false;
} }
#endif // ! defined (DISABLE_VERIFICATION) #endif // ! defined (DISABLE_VERIFICATION)
#endif // defined (HAVE_CREDS) #endif // defined (HAVE_CREDS)
}
return true; return true;
} }
@ -162,75 +171,114 @@ void Connection::closeConn()
{ {
if (m_fd != -1) if (m_fd != -1)
{ {
close(m_fd); if (!m_testMode)
{
close(m_fd);
}
m_fd = -1; m_fd = -1;
} }
} }
bool Connection::sendMsg(uint32_t msg) bool Connection::sendMsg(uint32_t msg)
{ {
Logger::logInfo("%s: %08x", __FUNCTION__, msg); if (!m_testMode)
return write(m_fd, &msg, sizeof(msg)) != -1; {
Logger::logInfo("%s: %08x", __FUNCTION__, msg);
return write(m_fd, &msg, sizeof(msg)) != -1;
}
else
{
return true;
}
} }
bool Connection::recvMsg(uint32_t *msg) bool Connection::recvMsg(uint32_t *msg)
{ {
uint32_t buf = 0; if (!m_testMode)
int len = sizeof(buf); {
ssize_t ret = read(m_fd, &buf, len); uint32_t buf = 0;
if (ret < len) { int len = sizeof(buf);
Logger::logError("Connection: can't read data from connecton in %s", __FUNCTION__); ssize_t ret = read(m_fd, &buf, len);
*msg = 0;
} else { if (ret < len)
Logger::logInfo("%s: %08x", __FUNCTION__, *msg); {
*msg = buf; Logger::logError("Connection: can't read data from connecton in %s", __FUNCTION__);
*msg = 0;
}
else
{
Logger::logInfo("%s: %08x", __FUNCTION__, *msg);
*msg = buf;
}
return ret != -1;
}
else
{
return true;
} }
return ret != -1;
} }
bool Connection::sendStr(const char * str) bool Connection::sendStr(const char * str)
{ {
// Send size. if (!m_testMode)
uint32_t size = strlen(str) + 1; {
sendMsg(size); // Send size.
uint32_t size = strlen(str) + 1;
sendMsg(size);
Logger::logInfo("Connection: %s: '%s'", __FUNCTION__, str); Logger::logInfo("Connection: %s: '%s'", __FUNCTION__, str);
// Send the string. // Send the string.
return write(m_fd, str, size) != -1; return write(m_fd, str, size) != -1;
}
else
{
return true;
}
} }
const char * Connection::recvStr() const char * Connection::recvStr()
{ {
// Get the size. if (!m_testMode)
uint32_t size = 0;
const uint32_t STR_LEN_MAX = 4096;
bool res = recvMsg(&size);
if (!res || size == 0 || size > STR_LEN_MAX)
{ {
Logger::logError("Connection: string receiving failed in %s, string length is %d", __FUNCTION__, size); // Get the size.
return NULL; uint32_t size = 0;
}
char * str = new char[size]; const uint32_t STR_LEN_MAX = 4096;
if (!str) bool res = recvMsg(&size);
{ if (!res || size == 0 || size > STR_LEN_MAX)
Logger::logError("Connection: mallocing in %s", __FUNCTION__); {
return NULL; Logger::logError("Connection: string receiving failed in %s, string length is %d", __FUNCTION__, size);
} return NULL;
}
char * str = new char[size];
if (!str)
{
Logger::logError("Connection: mallocing in %s", __FUNCTION__);
return NULL;
}
// Get the string.
uint32_t ret = read(m_fd, str, size);
if (ret < size)
{
Logger::logError("Connection: getting string, got %u of %u bytes", ret, size);
delete [] str;
return NULL;
}
// Get the string. str[size - 1] = '\0';
uint32_t ret = read(m_fd, str, size); Logger::logInfo("%s: '%s'", __FUNCTION__, str);
if (ret < size)
return str;
}
else
{ {
Logger::logError("Connection: getting string, got %u of %u bytes", ret, size);
delete [] str;
return NULL; return NULL;
} }
str[size - 1] = '\0';
Logger::logInfo("%s: '%s'", __FUNCTION__, str);
return str;
} }
bool Connection::sendPid(pid_t pid) bool Connection::sendPid(pid_t pid)

@ -49,7 +49,8 @@ typedef map<string, int> PoolType;
* \brief Wrapper class for the connection between invoker and launcher. * \brief Wrapper class for the connection between invoker and launcher.
* *
* This class wraps up the UNIX file socket connection between the invoker * This class wraps up the UNIX file socket connection between the invoker
* and the launcher daemon. * and the launcher daemon. The low-level communication code is mostly taken
* from the maemo-launcher used in Maemo 5. It might need a re-write.
*/ */
class Connection class Connection
{ {
@ -57,11 +58,9 @@ public:
/*! \brief Constructor. /*! \brief Constructor.
* \param socketId Path to the UNIX file socket to be used. * \param socketId Path to the UNIX file socket to be used.
* \param testMode Bypass all real socket activity to help unit testing.
*/ */
explicit Connection(const string socketId); explicit Connection(const string socketId, bool testMode = false);
//! \brief Destructor
virtual ~Connection();
/*! \brief Accept connection. /*! \brief Accept connection.
* Accept a socket connection from the invoker. * Accept a socket connection from the invoker.
@ -95,7 +94,6 @@ public:
//! \brief Get pid of the process on the other end of socket connection //! \brief Get pid of the process on the other end of socket connection
pid_t peerPid(); pid_t peerPid();
private: private:
/*! \brief Receive actions. /*! \brief Receive actions.
@ -155,12 +153,16 @@ private:
//! Send a string. This is a virtual to help unit testing. //! Send a string. This is a virtual to help unit testing.
virtual bool sendStr(const char * str); virtual bool sendStr(const char * str);
//! Receive a string. This is a virtual to help unit testing. //! Receive a string. This is a virtual to help unit testing.
virtual const char * recvStr(); virtual const char * recvStr();
//! Pool of sockets mapped to id's //! Pool of sockets mapped to id's
static PoolType socketPool; static PoolType socketPool;
//! Run in test mode, if true
bool m_testMode;
//! Socket fd //! Socket fd
int m_fd; int m_fd;
int m_curSocket; int m_curSocket;

@ -23,27 +23,22 @@
#include <sys/un.h> #include <sys/un.h>
#include <errno.h> #include <errno.h>
/* redefine some methods for Connection class */ // Redefine some methods for Connection class
class MyConnection : public Connection class MyConnection : public Connection
{ {
public: public:
int nextMsg; int nextMsg;
char* nextStr; char* nextStr;
MyConnection(const string socketId); MyConnection(const string socketId, bool testMode);
bool acceptConn();
private: private:
bool recvMsg(uint32_t *msg); bool recvMsg(uint32_t *msg);
const char * recvStr(); const char* recvStr();
bool sendMsg(uint32_t msg);
bool sendStr(const char * str);
}; };
bool MyConnection::acceptConn() { return true; } MyConnection::MyConnection(const string socketId, bool testMode) :
Connection(socketId, testMode),
MyConnection::MyConnection(const string socketId) :
Connection(socketId),
nextMsg(0), nextMsg(0),
nextStr(NULL) nextStr(NULL)
{} {}
@ -54,39 +49,25 @@ bool MyConnection::recvMsg(uint32_t *msg)
return true; return true;
} }
bool MyConnection::sendMsg(uint32_t) const char* MyConnection::recvStr()
{
return true;
}
bool MyConnection::sendStr(const char *)
{
return true;
}
const char * MyConnection::recvStr()
{ {
return nextStr; return nextStr;
} }
Ut_Connection::Ut_Connection() Ut_Connection::Ut_Connection()
{ {}
}
Ut_Connection::~Ut_Connection() Ut_Connection::~Ut_Connection()
{ {}
}
void Ut_Connection::initTestCase() void Ut_Connection::initTestCase()
{ {}
}
void Ut_Connection::cleanupTestCase() void Ut_Connection::cleanupTestCase()
{ {}
}
/* /*
* Check that socket initialized for provided socket id * Check that socket gets initialized for provided socket id
*/ */
void Ut_Connection::testInitConnection() void Ut_Connection::testInitConnection()
{ {
@ -106,28 +87,29 @@ void Ut_Connection::testInitConnection()
} }
/* /*
* Check that closeConn() reset socket connection * Test that socket creation / closing works.
*/ */
void Ut_Connection::testAcceptConnection() void Ut_Connection::testSocket()
{ {
char* socketName = (char*) "testAccept"; const char* socketName = "testAccept";
Connection::initSocket(socketName); Connection::initSocket(socketName);
MyConnection* conn = new MyConnection(socketName); MyConnection* conn = new MyConnection(socketName, false);
conn->m_fd = 1000; conn->m_fd = 1000;
QVERIFY(conn->acceptConn() == true);
QVERIFY(conn->m_fd > 0); QVERIFY(conn->m_fd > 0);
conn->closeConn(); conn->closeConn();
QVERIFY(conn->m_fd == -1); QVERIFY(conn->m_fd == -1);
unlink("testAccept"); unlink(socketName);
} }
/* /*
* Check that env variable passed from invoker will * Check that env variable passed from invoker will
* be set in launcher process * be set in launcher process.
*
* Run in the test mode (no sockets really created).
*/ */
void Ut_Connection::testGetEnv() void Ut_Connection::testGetEnv()
{ {
@ -135,8 +117,7 @@ void Ut_Connection::testGetEnv()
QVERIFY(getenv("PATH") != NULL); QVERIFY(getenv("PATH") != NULL);
const char* socketName = "testGetEnv"; const char* socketName = "testGetEnv";
Connection::initSocket(socketName); MyConnection* conn = new MyConnection(socketName, true);
MyConnection* conn = new MyConnection(socketName);
char* envVar = strdup("MY_TEST_ENV_VAR=3"); char* envVar = strdup("MY_TEST_ENV_VAR=3");
@ -147,33 +128,31 @@ void Ut_Connection::testGetEnv()
QVERIFY(getenv("MY_TEST_ENV_VAR") != NULL); QVERIFY(getenv("MY_TEST_ENV_VAR") != NULL);
QVERIFY(getenv("PATH") != NULL); QVERIFY(getenv("PATH") != NULL);
unlink(socketName);
delete envVar; delete envVar;
} }
/* /*
* Check getAppName() function correctness * Check getAppName() function correctness
*
* Run in the test mode (no sockets really created).
*/ */
void Ut_Connection::testGetAppName() void Ut_Connection::testGetAppName()
{ {
const char* socketName = "testGetAppName"; const char* socketName = "testGetAppName";
MyConnection* conn = new MyConnection(socketName, true);
Connection::initSocket(socketName); // Wrong type of message
MyConnection* conn = new MyConnection(socketName);
// wrong type of message
conn->nextMsg = INVOKER_MSG_EXEC; conn->nextMsg = INVOKER_MSG_EXEC;
string wrongStr = conn->receiveAppName(); string wrongStr = conn->receiveAppName();
QVERIFY(wrongStr.empty()); QVERIFY(wrongStr.empty());
// empty app name // Empty app name
conn->nextMsg = INVOKER_MSG_NAME; conn->nextMsg = INVOKER_MSG_NAME;
conn->nextStr = NULL; conn->nextStr = NULL;
string emptyName = conn->receiveAppName(); string emptyName = conn->receiveAppName();
QVERIFY(emptyName.empty()); QVERIFY(emptyName.empty());
// real name // Real name
string realName("looooongApplicationName"); string realName("looooongApplicationName");
char* dupName = strdup(realName.c_str()); char* dupName = strdup(realName.c_str());
@ -183,8 +162,6 @@ void Ut_Connection::testGetAppName()
string resName = conn->receiveAppName(); string resName = conn->receiveAppName();
QVERIFY(!resName.empty()); QVERIFY(!resName.empty());
QVERIFY(resName.compare(realName) == 0); QVERIFY(resName.compare(realName) == 0);
unlink(socketName);
} }
QTEST_APPLESS_MAIN(Ut_Connection); QTEST_APPLESS_MAIN(Ut_Connection);

@ -42,7 +42,7 @@ private Q_SLOTS:
void initTestCase(); void initTestCase();
void cleanupTestCase(); void cleanupTestCase();
void testInitConnection(); void testInitConnection();
void testAcceptConnection(); void testSocket();
void testGetEnv(); void testGetEnv();
void testGetAppName(); void testGetAppName();

Loading…
Cancel
Save