From 89486aaa187165127629f9d07b52d0d129132902 Mon Sep 17 00:00:00 2001
From: Pawel Kubik
Date: Fri, 18 Sep 2015 15:03:33 +0200
Subject: [PATCH] IPC unit tests and testing framework improvements
[Feature] 1. IPC unit tests
2. Cleaner args parsing in vsm_launch_test.py
3. Options to launch a test under custom tools
[Cause] 1. N/A
2. Bugs when launching with external tool
3. Ability to launch any GDB front-end
[Solution] 1. N/A
2. Use list instead of string in Popen
3. Two new command line options
[Verification] Build, install run tests. Try to run single test with
a fixture, for example:
vsm_launch_test.py vasum-server-unit-tests -t \
'IPCSuite/Restart<15ThreadedFixture>'
To test running with --gdb option run test logged as
root or use sudo with -E option in order to preserve
environment.
Change-Id: Icb09c0abed5c671c86a8c85d2aab1aa2b2412d29
---
common/utils/eventfd.cpp | 2 +-
common/utils/exception.hpp | 5 ++
libs/ipc/exception.hpp | 19 +++++
libs/ipc/internals/processor.cpp | 2 +-
libs/ipc/internals/socket.cpp | 4 +-
libs/ipc/unique-id.cpp | 4 +-
tests/scripts/vsm_launch_test.py | 32 +++----
tests/unit_tests/ipc/ut-ipc.cpp | 149 +++++++++++++++++++++++----------
tests/unit_tests/ipc/ut-unique-id.cpp | 19 +++++
tests/unit_tests/utils/ut-fd-utils.cpp | 51 +++++++++++
10 files changed, 219 insertions(+), 68 deletions(-)
create mode 100644 tests/unit_tests/utils/ut-fd-utils.cpp
diff --git a/common/utils/eventfd.cpp b/common/utils/eventfd.cpp
index bc5e64a..627b773 100644
--- a/common/utils/eventfd.cpp
+++ b/common/utils/eventfd.cpp
@@ -42,7 +42,7 @@ EventFD::EventFD()
if (mFD == -1) {
const std::string msg = "Error in eventfd: " + getSystemErrorMessage();
LOGE(msg);
- throw UtilsException(msg);
+ throw EventFDException(msg);
}
}
diff --git a/common/utils/exception.hpp b/common/utils/exception.hpp
index 232b090..01bb380 100644
--- a/common/utils/exception.hpp
+++ b/common/utils/exception.hpp
@@ -39,6 +39,11 @@ struct UtilsException: public std::runtime_error {
explicit UtilsException(const std::string& error) : std::runtime_error(error) {}
};
+struct EventFDException: public UtilsException {
+
+ explicit EventFDException(const std::string& error) : UtilsException(error) {}
+};
+
struct ProvisionExistsException: public UtilsException {
explicit ProvisionExistsException(const std::string& error) : UtilsException(error) {}
diff --git a/libs/ipc/exception.hpp b/libs/ipc/exception.hpp
index 3fe7a5b..f757bcf 100644
--- a/libs/ipc/exception.hpp
+++ b/libs/ipc/exception.hpp
@@ -104,6 +104,25 @@ struct IPCTimeoutException: public IPCException {
};
/**
+ * Exception to indicate socket error
+ * @ingroup IPCException
+ */
+struct IPCSocketException: public IPCException {
+ IPCSocketException(const int code, const std::string& message)
+ : IPCException(message),
+ mCode(code)
+ {}
+
+ int getCode() const
+ {
+ return mCode;
+ }
+
+private:
+ int mCode;
+};
+
+/**
* Exception to indicate user error
* @ingroup IPCException
*/
diff --git a/libs/ipc/internals/processor.cpp b/libs/ipc/internals/processor.cpp
index 164d735..8cfeed1 100644
--- a/libs/ipc/internals/processor.cpp
+++ b/libs/ipc/internals/processor.cpp
@@ -130,7 +130,7 @@ void Processor::stop(bool wait)
mRequestQueue.pushBack(Event::FINISH, request);
}
- if(wait){
+ if (wait) {
LOGD(mLogPrefix + "Waiting for the Processor to stop");
// Wait till the FINISH request is served
diff --git a/libs/ipc/internals/socket.cpp b/libs/ipc/internals/socket.cpp
index a88ff0d..9bdfd35 100644
--- a/libs/ipc/internals/socket.cpp
+++ b/libs/ipc/internals/socket.cpp
@@ -150,7 +150,7 @@ int Socket::createSocketInternal(const std::string& path)
if (sockfd == -1) {
const std::string msg = "Error in socket: " + getSystemErrorMessage();
LOGE(msg);
- throw IPCException(msg);
+ throw IPCSocketException(errno, msg);
}
setFdOptions(sockfd);
@@ -210,7 +210,7 @@ Socket Socket::connectSocket(const std::string& path)
if (fd == -1) {
const std::string msg = "Error in socket: " + getSystemErrorMessage();
LOGE(msg);
- throw IPCException(msg);
+ throw IPCSocketException(errno, msg);
}
setFdOptions(fd);
diff --git a/libs/ipc/unique-id.cpp b/libs/ipc/unique-id.cpp
index bcdd4bc..4a121ec 100644
--- a/libs/ipc/unique-id.cpp
+++ b/libs/ipc/unique-id.cpp
@@ -55,9 +55,7 @@ UniqueID::operator std::string() const
std::ostream& operator<<(std::ostream& str, const UniqueID& id)
{
- char uuid[37];
- ::uuid_unparse(id.mUUID, uuid);
- str << id.mTime.tv_sec << "." << id.mTime.tv_nsec << ":" << uuid;
+ str << static_cast(id);
return str;
}
diff --git a/tests/scripts/vsm_launch_test.py b/tests/scripts/vsm_launch_test.py
index dd5831f..b4fe529 100755
--- a/tests/scripts/vsm_launch_test.py
+++ b/tests/scripts/vsm_launch_test.py
@@ -39,20 +39,15 @@ def launchTest(cmd=[], externalToolCmd=[], parsing=True):
if externalToolCmd and not _checkIfBinExists(externalToolCmd[0]):
return
- cmd[1:] = ["'{0}'".format(arg) if re.search("^\s*[^']*/.*<.*>\s*$", arg)
- else arg
- for arg in cmd[1:]]
-
log.info("Starting " + cmd[0] + " ...")
if parsing:
parser = Parser()
- command = " ".join(externalToolCmd + cmd + _defLaunchArgs)
- log.info("Invoking `" + command + "`")
- p = subprocess.Popen(command,
- shell=True,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ commandString = " ".join(externalToolCmd + cmd + _defLaunchArgs)
+ log.info("Invoking `" + commandString + "`")
+ p = subprocess.Popen(externalToolCmd + cmd + _defLaunchArgs,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
testResult = parser.parseOutputFromProcess(p)
if testResult != "":
domResult = minidom.parseString(testResult)
@@ -63,10 +58,9 @@ def launchTest(cmd=[], externalToolCmd=[], parsing=True):
else:
# Launching process without coloring does not require report in XML form
# Avoid providing --report_format=XML, redirect std* by default to system's std*
- command = " ".join(externalToolCmd + cmd + _defLaunchArgs[1:])
- log.info("Invoking `" + command + "`")
- p = subprocess.Popen(command,
- shell=True)
+ commandString = " ".join(externalToolCmd + cmd + _defLaunchArgs[1:])
+ log.info("Invoking `" + commandString + "`")
+ p = subprocess.Popen(externalToolCmd + cmd + _defLaunchArgs[1:])
p.wait()
log.info(cmd[0] + " finished.")
@@ -82,7 +76,8 @@ def main():
group.add_argument('--valgrind', action='store_true',
help='Launch test binary inside Valgrind (assuming it is installed).')
group.add_argument('--gdb', action='store_true',
- help='Launch test binary with GDB (assuming it is installed).')
+ help='Launch test binary with a tool specified by $VSM_DEBUGGER variable. '
+ +'Defaults to gdb.')
argparser.add_argument('binary', nargs=argparse.REMAINDER,
help='Binary to be launched using script.')
@@ -90,7 +85,12 @@ def main():
if args[0].binary:
if args[0].gdb:
- launchTest(args[0].binary, externalToolCmd=_gdbCmd + args[1], parsing=False)
+ debuggerVar = os.getenv("VSM_DEBUGGER")
+ if (debuggerVar):
+ _customDebuggerCmd = debuggerVar.split()
+ else:
+ _customDebuggerCmd = _gdbCmd
+ launchTest(args[0].binary, externalToolCmd=_customDebuggerCmd + args[1], parsing=False)
elif args[0].valgrind:
launchTest(args[0].binary, externalToolCmd=_valgrindCmd + args[1])
else:
diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp
index 5ba540e..0e16806 100644
--- a/tests/unit_tests/ipc/ut-ipc.cpp
+++ b/tests/unit_tests/ipc/ut-ipc.cpp
@@ -37,6 +37,7 @@
#include "ipc/result.hpp"
#include "ipc/epoll/thread-dispatcher.hpp"
#include "ipc/epoll/glib-dispatcher.hpp"
+#include "utils/channel.hpp"
#include "utils/glib-loop.hpp"
#include "utils/latch.hpp"
#include "utils/value-latch.hpp"
@@ -53,8 +54,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
@@ -207,7 +210,7 @@ void shortEchoCallback(const PeerID,
methodResult->set(returnData);
}
-PeerID connect(Service& s, Client& c)
+PeerID connectPeer(Service& s, Client& c)
{
// Connects the Client to the Service and returns Clients PeerID
ValueLatch peerIDLatch;
@@ -265,7 +268,7 @@ MULTI_FIXTURE_TEST_CASE(ServiceAddRemoveMethod, F, ThreadedFixture, GlibFixture)
s.setMethodHandler(2, returnDataCallback);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
testEcho(c, 1);
s.removeMethod(1);
@@ -281,7 +284,7 @@ MULTI_FIXTURE_TEST_CASE(ClientAddRemoveMethod, F, ThreadedFixture, GlibFixture)
c.setMethodHandler(1, returnEmptyCallback);
c.setMethodHandler(1, returnDataCallback);
- PeerID peerID = connect(s, c);
+ PeerID peerID = connectPeer(s, c);
c.setMethodHandler(1, echoCallback);
c.setMethodHandler(2, returnDataCallback);
@@ -294,6 +297,29 @@ MULTI_FIXTURE_TEST_CASE(ClientAddRemoveMethod, F, ThreadedFixture, GlibFixture)
BOOST_CHECK_THROW(testEcho(s, 1, peerID), IPCException);
}
+MULTI_FIXTURE_TEST_CASE(MethodResultGetPeerID, F, ThreadedFixture, GlibFixture)
+{
+ Service s(F::getPoll(), SOCKET_PATH);
+ Client c(F::getPoll(), SOCKET_PATH);
+
+ PeerID peerID = connectPeer(s, c);
+
+ s.setMethodHandler(
+ 1,
+ [&peerID](const PeerID,
+ std::shared_ptr&,
+ MethodResult::Pointer methodResult) {
+ methodResult->setVoid();
+ BOOST_CHECK_EQUAL(peerID, methodResult->getPeerID());
+ }
+ );
+
+ std::shared_ptr sentData(new SendData(32));
+ std::shared_ptr recvData = c.callSync(1,
+ sentData,
+ TIMEOUT);
+}
+
MULTI_FIXTURE_TEST_CASE(ServiceStartStop, F, ThreadedFixture, GlibFixture)
{
Service s(F::getPoll(), SOCKET_PATH);
@@ -334,7 +360,7 @@ MULTI_FIXTURE_TEST_CASE(SyncClientToServiceEcho, F, ThreadedFixture, GlibFixture
s.setMethodHandler(2, echoCallback);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
testEcho(c, 1);
testEcho(c, 2);
@@ -375,7 +401,7 @@ MULTI_FIXTURE_TEST_CASE(SyncServiceToClientEcho, F, ThreadedFixture, GlibFixture
Service s(F::getPoll(), SOCKET_PATH);
Client c(F::getPoll(), SOCKET_PATH);
c.setMethodHandler(1, echoCallback);
- PeerID peerID = connect(s, c);
+ PeerID peerID = connectPeer(s, c);
std::shared_ptr sentData(new SendData(56));
std::shared_ptr recvData = s.callSync(1, peerID, sentData);
@@ -414,7 +440,7 @@ MULTI_FIXTURE_TEST_CASE(AsyncServiceToClientEcho, F, ThreadedFixture, GlibFixtur
Service s(F::getPoll(), SOCKET_PATH);
Client c(F::getPoll(), SOCKET_PATH);
c.setMethodHandler(1, echoCallback);
- PeerID peerID = connect(s, c);
+ PeerID peerID = connectPeer(s, c);
// Async call
auto dataBack = [&recvDataLatch](Result && r) {
@@ -435,7 +461,7 @@ MULTI_FIXTURE_TEST_CASE(SyncTimeout, F, ThreadedFixture, GlibFixture)
s.setMethodHandler(1, longEchoCallback);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
std::shared_ptr sentData(new SendData(78));
BOOST_REQUIRE_THROW((c.callSync(1, sentData, TIMEOUT)), IPCException);
@@ -447,7 +473,7 @@ MULTI_FIXTURE_TEST_CASE(SerializationError, F, ThreadedFixture, GlibFixture)
s.setMethodHandler(1, echoCallback);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
std::shared_ptr throwingData(new ThrowOnAcceptData());
@@ -513,7 +539,7 @@ MULTI_FIXTURE_TEST_CASE(ReadTimeout, F, ThreadedFixture, GlibFixture)
s.setMethodHandler(1, longEchoCallback);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
// Test timeout on read
std::shared_ptr sentData(new SendData(334));
@@ -549,7 +575,7 @@ MULTI_FIXTURE_TEST_CASE(AddSignalInRuntime, F, ThreadedFixture, GlibFixture)
Service s(F::getPoll(), SOCKET_PATH);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
auto handlerA = [&recvDataLatchA](const PeerID, std::shared_ptr& data) {
recvDataLatchA.set(data);
@@ -597,7 +623,7 @@ MULTI_FIXTURE_TEST_CASE(AddSignalOffline, F, ThreadedFixture, GlibFixture)
c.setSignalHandler(1, handlerA);
c.setSignalHandler(2, handlerB);
- connect(s, c);
+ connectPeer(s, c);
// Wait for the information about the signals to propagate
std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT));
@@ -621,7 +647,7 @@ MULTI_FIXTURE_TEST_CASE(UsersError, F, ThreadedFixture, GlibFixture)
Service s(F::getPoll(), SOCKET_PATH);
Client c(F::getPoll(), SOCKET_PATH);
- auto clientID = connect(s, c);
+ auto clientID = connectPeer(s, c);
auto throwingMethodHandler = [&](const PeerID, std::shared_ptr&, MethodResult::Pointer) {
throw IPCUserException(TEST_ERROR_CODE, TEST_ERROR_MESSAGE);
@@ -655,7 +681,7 @@ MULTI_FIXTURE_TEST_CASE(AsyncResult, F, ThreadedFixture, GlibFixture)
Service s(F::getPoll(), SOCKET_PATH);
Client c(F::getPoll(), SOCKET_PATH);
- auto clientID = connect(s, c);
+ auto clientID = connectPeer(s, c);
auto errorMethodHandler = [&](const PeerID, std::shared_ptr&, MethodResult::Pointer methodResult) {
std::async(std::launch::async, [&, methodResult] {
@@ -718,7 +744,7 @@ MULTI_FIXTURE_TEST_CASE(MixOperations, F, ThreadedFixture, GlibFixture)
Client c(F::getPoll(), SOCKET_PATH);
s.setSignalHandler(2, signalHandler);
- connect(s, c);
+ connectPeer(s, c);
testEcho(c, 1);
@@ -749,7 +775,7 @@ MULTI_FIXTURE_TEST_CASE(FDSendReceive, F, ThreadedFixture, GlibFixture)
s.setMethodHandler(1, methodHandler);
Client c(F::getPoll(), SOCKET_PATH);
- connect(s, c);
+ connectPeer(s, c);
std::shared_ptr fdData;
std::shared_ptr sentData(new EmptyData());
@@ -762,36 +788,69 @@ MULTI_FIXTURE_TEST_CASE(FDSendReceive, F, ThreadedFixture, GlibFixture)
::close(fdData->fd.value);
}
-// MULTI_FIXTURE_TEST_CASE(ConnectionLimit, F, ThreadedFixture, GlibFixture)
-// {
-// unsigned oldLimit = ipc::getMaxFDNumber();
-// ipc::setMaxFDNumber(50);
-
-// // Setup Service and many Clients
-// Service s(F::getPoll(), SOCKET_PATH);
-// s.setMethodHandler(1, echoCallback);
-// s.start();
-
-// std::list clients;
-// for (int i = 0; i < 100; ++i) {
-// try {
-// clients.push_back(Client(F::getPoll(), SOCKET_PATH));
-// clients.back().start();
-// } catch (...) {}
-// }
-
-// unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
-// std::mt19937 generator(seed);
-// for (auto it = clients.begin(); it != clients.end(); ++it) {
-// try {
-// std::shared_ptr sentData(new SendData(generator()));
-// std::shared_ptr recvData = it->callSync(1, sentData);
-// BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
-// } catch (...) {}
-// }
-
-// ipc::setMaxFDNumber(oldLimit);
-// }
+BOOST_AUTO_TEST_CASE(ConnectionLimit)
+{
+ const unsigned oldLimit = utils::getMaxFDNumber();
+ const unsigned newLimit = 32;
+ ScopedDir scopedDir(TEST_DIR);
+
+ Channel c;
+
+ const pid_t chpid = ::fork();
+ BOOST_CHECK_NE(chpid, -1);
+
+ if (chpid) {
+ // Setup Service
+ ThreadDispatcher td;
+ Service s(td.getPoll(), SOCKET_PATH);
+ s.setMethodHandler(1, echoCallback);
+ s.start();
+
+ c.setLeft();
+ try {
+ // inform the Client
+ c.write(true);
+ } catch (...) {
+ kill(chpid, 9);
+ throw;
+ }
+
+ int status;
+ BOOST_CHECK_EQUAL(::waitpid(chpid, &status, 0), chpid);
+ BOOST_CHECK_EQUAL(status, EXIT_SUCCESS);
+ } else {
+ int ret = EXIT_FAILURE;
+ utils::setMaxFDNumber(newLimit);
+
+ c.setRight();
+ try {
+ // wait for the Service
+ c.read();
+ } catch(...) {
+ ::_exit(EXIT_FAILURE);
+ }
+
+ // Setup Clients
+ ThreadDispatcher td;
+ std::list clients;
+ try {
+ for (unsigned i = 0; i < 2 * newLimit; ++i) {
+ clients.emplace_back(td.getPoll(), SOCKET_PATH);
+ clients.back().start();
+ }
+ } catch (const EventFDException& e) {
+ ret = EXIT_SUCCESS;
+ } catch (const IPCSocketException& e) {
+ if (e.getCode() == EMFILE) {
+ ret = EXIT_SUCCESS;
+ }
+ }
+
+ utils::setMaxFDNumber(oldLimit);
+
+ ::_exit(ret);
+ }
+}
diff --git a/tests/unit_tests/ipc/ut-unique-id.cpp b/tests/unit_tests/ipc/ut-unique-id.cpp
index 05ce1bb..e60bc88 100644
--- a/tests/unit_tests/ipc/ut-unique-id.cpp
+++ b/tests/unit_tests/ipc/ut-unique-id.cpp
@@ -29,6 +29,7 @@
#include "ipc/unique-id.hpp"
#include
+#include
#include
namespace {
@@ -70,4 +71,22 @@ BOOST_AUTO_TEST_CASE(DoubleGenerate)
BOOST_CHECK_NE(uid1, uid2);
}
+// compare two empty UIDs
+BOOST_AUTO_TEST_CASE(EmptyCompare)
+{
+ ipc::UniqueID uid1, uid2;
+
+ BOOST_CHECK_EQUAL(uid1, uid2);
+}
+
+// pass empty UID to a stream
+BOOST_AUTO_TEST_CASE(StreamOperator)
+{
+ ipc::UniqueID uid;
+ std::stringstream ss;
+
+ ss << uid;
+ BOOST_CHECK_EQUAL(ss.str(), "0.0:" + EMPTY_UUID);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-fd-utils.cpp b/tests/unit_tests/utils/ut-fd-utils.cpp
new file mode 100644
index 0000000..3c0962d
--- /dev/null
+++ b/tests/unit_tests/utils/ut-fd-utils.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+
+/**
+ * @file
+ * @author Pawel Kubik (p.kubik@samsung.com)
+ * @brief Unit tests of fd utils
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "utils/fd-utils.hpp"
+
+#include "logger/logger.hpp"
+
+
+using namespace utils;
+
+
+BOOST_AUTO_TEST_SUITE(FDUtilsSuite)
+
+BOOST_AUTO_TEST_CASE(GetSetMaxFDNumber)
+{
+ unsigned oldLimit = utils::getMaxFDNumber();
+ unsigned newLimit = 50;
+
+ utils::setMaxFDNumber(newLimit);
+ BOOST_CHECK_EQUAL(newLimit, utils::getMaxFDNumber());
+
+ utils::setMaxFDNumber(oldLimit);
+ BOOST_CHECK_EQUAL(oldLimit, utils::getMaxFDNumber());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
--
2.7.4