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