IPC unit tests and testing framework improvements 77/48677/14
authorPawel Kubik <p.kubik@samsung.com>
Fri, 18 Sep 2015 13:03:33 +0000 (15:03 +0200)
committerDariusz Michaluk <d.michaluk@samsung.com>
Wed, 7 Oct 2015 14:24:17 +0000 (07:24 -0700)
[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
common/utils/exception.hpp
libs/ipc/exception.hpp
libs/ipc/internals/processor.cpp
libs/ipc/internals/socket.cpp
libs/ipc/unique-id.cpp
tests/scripts/vsm_launch_test.py
tests/unit_tests/ipc/ut-ipc.cpp
tests/unit_tests/ipc/ut-unique-id.cpp
tests/unit_tests/utils/ut-fd-utils.cpp [new file with mode: 0644]

index bc5e64a..627b773 100644 (file)
@@ -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);
     }
 }
 
index 232b090..01bb380 100644 (file)
@@ -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) {}
index 3fe7a5b..f757bcf 100644 (file)
@@ -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
  */
index 164d735..8cfeed1 100644 (file)
@@ -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
index a88ff0d..9bdfd35 100644 (file)
@@ -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);
 
index bcdd4bc..4a121ec 100644 (file)
@@ -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<std::string>(id);
     return str;
 }
 
index dd5831f..b4fe529 100755 (executable)
@@ -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:
index 5ba540e..0e16806 100644 (file)
@@ -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"
 #include <chrono>
 #include <utility>
 #include <future>
+#include <semaphore.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 #include <fcntl.h>
 
 
@@ -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<PeerID> peerIDLatch;
@@ -265,7 +268,7 @@ MULTI_FIXTURE_TEST_CASE(ServiceAddRemoveMethod, F, ThreadedFixture, GlibFixture)
     s.setMethodHandler<SendData, RecvData>(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<EmptyData, EmptyData>(1, returnEmptyCallback);
     c.setMethodHandler<SendData, RecvData>(1, returnDataCallback);
 
-    PeerID peerID = connect(s, c);
+    PeerID peerID = connectPeer(s, c);
 
     c.setMethodHandler<SendData, RecvData>(1, echoCallback);
     c.setMethodHandler<SendData, RecvData>(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<SendData, RecvData>(
+        1,
+        [&peerID](const PeerID,
+                  std::shared_ptr<RecvData>&,
+                  MethodResult::Pointer methodResult) {
+            methodResult->setVoid();
+            BOOST_CHECK_EQUAL(peerID, methodResult->getPeerID());
+        }
+    );
+
+    std::shared_ptr<SendData> sentData(new SendData(32));
+    std::shared_ptr<RecvData> recvData = c.callSync<SendData, RecvData>(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<SendData, RecvData>(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<SendData, RecvData>(1, echoCallback);
-    PeerID peerID = connect(s, c);
+    PeerID peerID = connectPeer(s, c);
 
     std::shared_ptr<SendData> sentData(new SendData(56));
     std::shared_ptr<RecvData> recvData = s.callSync<SendData, RecvData>(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<SendData, RecvData>(1, echoCallback);
-    PeerID peerID = connect(s, c);
+    PeerID peerID = connectPeer(s, c);
 
     // Async call
     auto dataBack = [&recvDataLatch](Result<RecvData> && r) {
@@ -435,7 +461,7 @@ MULTI_FIXTURE_TEST_CASE(SyncTimeout, F, ThreadedFixture, GlibFixture)
     s.setMethodHandler<SendData, RecvData>(1, longEchoCallback);
 
     Client c(F::getPoll(), SOCKET_PATH);
-    connect(s, c);
+    connectPeer(s, c);
 
     std::shared_ptr<SendData> sentData(new SendData(78));
     BOOST_REQUIRE_THROW((c.callSync<SendData, RecvData>(1, sentData, TIMEOUT)), IPCException);
@@ -447,7 +473,7 @@ MULTI_FIXTURE_TEST_CASE(SerializationError, F, ThreadedFixture, GlibFixture)
     s.setMethodHandler<SendData, RecvData>(1, echoCallback);
 
     Client c(F::getPoll(), SOCKET_PATH);
-    connect(s, c);
+    connectPeer(s, c);
 
     std::shared_ptr<ThrowOnAcceptData> throwingData(new ThrowOnAcceptData());
 
@@ -513,7 +539,7 @@ MULTI_FIXTURE_TEST_CASE(ReadTimeout, F, ThreadedFixture, GlibFixture)
     s.setMethodHandler<LongSendData, RecvData>(1, longEchoCallback);
 
     Client c(F::getPoll(), SOCKET_PATH);
-    connect(s, c);
+    connectPeer(s, c);
 
     // Test timeout on read
     std::shared_ptr<SendData> 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<RecvData>& data) {
         recvDataLatchA.set(data);
@@ -597,7 +623,7 @@ MULTI_FIXTURE_TEST_CASE(AddSignalOffline, F, ThreadedFixture, GlibFixture)
     c.setSignalHandler<RecvData>(1, handlerA);
     c.setSignalHandler<RecvData>(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<RecvData>&, 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<RecvData>&, 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<RecvData>(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<FDData, EmptyData>(1, methodHandler);
 
     Client c(F::getPoll(), SOCKET_PATH);
-    connect(s, c);
+    connectPeer(s, c);
 
     std::shared_ptr<FDData> fdData;
     std::shared_ptr<EmptyData> 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<SendData, RecvData>(1, echoCallback);
-//     s.start();
-
-//     std::list<Client> 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<SendData> sentData(new SendData(generator()));
-//             std::shared_ptr<RecvData> recvData = it->callSync<SendData, RecvData>(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<SendData, RecvData>(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<char>();
+        } catch(...) {
+            ::_exit(EXIT_FAILURE);
+        }
+
+        // Setup Clients
+        ThreadDispatcher td;
+        std::list<Client> 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);
+    }
+}
 
 
 
index 05ce1bb..e60bc88 100644 (file)
@@ -29,6 +29,7 @@
 #include "ipc/unique-id.hpp"
 
 #include <string>
+#include <sstream>
 #include <uuid/uuid.h>
 
 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 (file)
index 0000000..3c0962d
--- /dev/null
@@ -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()