Enhance cynara commandline parameters 59/38659/4
authorLukasz Wojciechowski <l.wojciechow@partner.samsung.com>
Thu, 23 Apr 2015 15:40:37 +0000 (17:40 +0200)
committerLukasz Wojciechowski <l.wojciechow@partner.samsung.com>
Fri, 24 Apr 2015 13:23:08 +0000 (15:23 +0200)
Now cynara can get additional command line parameters:
-d, --daemon > for daemonization
-m, --mask > for setting umask
-u, --user > for changing uid
-g, --gid > for changing group

Additional unit tests covering parsing these parameters were added.
So far additinal options do nothing. Following patches will make
use of parsed options.

Change-Id: I3bcc678ed66e5e2659078711f4f33445e3556c45

src/service/main/CmdlineParser.cpp
src/service/main/CmdlineParser.h
src/service/main/main.cpp
test/service/main/cmdlineparser.cpp

index cd8e4cb..ca56e39 100644 (file)
  * @brief       Helper namespace for Cynara's command-line options parsing
  */
 
+#include <cstdlib>
 #include <getopt.h>
+#include <grp.h>
 #include <iostream>
+#include <pwd.h>
 #include <sstream>
 
 #include "CmdlineParser.h"
@@ -34,17 +37,33 @@ std::ostream &operator<<(std::ostream &os, CmdlineOpt opt) {
     return os << static_cast<char>(opt);
 }
 
-bool handleCmdlineOptions(int argc, char * const *argv) {
+struct CmdLineOptions handleCmdlineOptions(int argc, char * const *argv) {
     const std::string execName(argv[0]);
     std::stringstream shortOpts;
-    shortOpts << ":" << CmdlineOpt::Help << CmdlineOpt::Version;
+    shortOpts << ":"
+              << CmdlineOpt::Help
+              << CmdlineOpt::Version
+              << CmdlineOpt::Daemon
+              << CmdlineOpt::Mask << ":"
+              << CmdlineOpt::User << ":"
+              << CmdlineOpt::Group << ":";
 
     const struct option longOpts[] = {
-        { "help",       no_argument,    NULL, CmdlineOpt::Help },
-        { "version",    no_argument,    NULL, CmdlineOpt::Version },
+        { "help",       no_argument,          NULL, CmdlineOpt::Help },
+        { "version",    no_argument,          NULL, CmdlineOpt::Version },
+        { "daemon",     no_argument,          NULL, CmdlineOpt::Daemon },
+        { "mask",       required_argument,    NULL, CmdlineOpt::Mask },
+        { "user",       required_argument,    NULL, CmdlineOpt::User },
+        { "group",      required_argument,    NULL, CmdlineOpt::Group },
         { NULL, 0, NULL, 0 }
     };
 
+    struct CmdLineOptions ret = {.m_error = false,
+                                 .m_exit = false,
+                                 .m_daemon = false,
+                                 .m_mask = static_cast<mode_t>(-1),
+                                 .m_uid = static_cast<uid_t>(-1),
+                                 .m_gid = static_cast<gid_t>(-1) };
 
     optind = 0; // On entry to `getopt', zero means this is the first call; initialize.
     int opt;
@@ -52,42 +71,142 @@ bool handleCmdlineOptions(int argc, char * const *argv) {
         switch (opt) {
             case CmdlineOpt::Help:
                 printHelp(execName);
-                return true;
+                ret.m_error = false;
+                ret.m_exit = true;
+                return ret;
             case CmdlineOpt::Version:
                 printVersion();
-                return true;
+                ret.m_error = false;
+                ret.m_exit = true;
+                return ret;
+            case CmdlineOpt::Daemon:
+                ret.m_daemon = true;
+                break;
+            case CmdlineOpt::Mask:
+                ret.m_mask = getMask(optarg);
+                if (ret.m_mask == static_cast<mode_t>(-1)) {
+                    printInvalidParam(execName, optarg);
+                    ret.m_error = true;
+                    ret.m_exit = true;
+                    return ret;
+                }
+                break;
+            case CmdlineOpt::User:
+                ret.m_uid = getUid(optarg);
+                if (ret.m_uid == static_cast<uid_t>(-1)) {
+                    printInvalidParam(execName, optarg);
+                    ret.m_error = true;
+                    ret.m_exit = true;
+                    return ret;
+                }
+                break;
+            case CmdlineOpt::Group:
+                ret.m_gid = getGid(optarg);
+                if (ret.m_gid == static_cast<gid_t>(-1)) {
+                    printInvalidParam(execName, optarg);
+                    ret.m_error = true;
+                    ret.m_exit = true;
+                    return ret;
+                }
+                break;
+            case ':': // Missing argument
+                ret.m_error = true;
+                ret.m_exit = true;
+                switch (optopt) {
+                    case CmdlineOpt::Mask:
+                    case CmdlineOpt::User:
+                    case CmdlineOpt::Group:
+                        printMissingArgument(execName, argv[optind - 1]);
+                        return ret;
+                }
+                //intentional fall to Unknown option
             case '?': // Unknown option
             default:
-                printUnknownOption(execName);
-                return false;
+                printUnknownOption(execName, argv[optind - 1]);
+                ret.m_error = true;
+                ret.m_exit = true;
+                return ret;
         }
     }
 
-    printNoOptions(execName);
-    return false;
+    return ret;
 }
 
 void printHelp(const std::string &execName) {
     std::cout << "Usage: " << execName << " [OPTIONS]" << std::endl << std::endl;
-    std::cout << "  -V, --version                  print version of " << execName << " and exit"
-              << std::endl;
-    std::cout << "  -h, --help                     print this help message and exit" << std::endl;
+    std::cout << "Information mode options [program exits after printing information]:" << std::endl;
+    std::cout << "  -V, --version                print version of " << execName << " and exit"
+                 << std::endl;
+    std::cout << "  -h, --help                   print this help message and exit" << std::endl;
+    std::cout << "Normal work mode options:" << std::endl;
+    std::cout << "  -d, --daemon                 daemonize "
+                 "[by default " << execName << " does not daemonize]" << std::endl;
+    std::cout << "  -m, --mask=MASK              set umask to MASK "
+                 "[by default no umask is set]" << std::endl;
+    std::cout << "  -u, --user=USER              change user to USER "
+                 "[by default uid is not changed]" << std::endl;
+    std::cout << "  -g, --group=GROUP            change group to GROUP "
+                 "[by default gid is not changed]" << std::endl;
 }
 
 void printVersion(void) {
     std::cout << std::string(CYNARA_VERSION) << std::endl;
 }
 
-void printUnknownOption(const std::string &execName) {
-    std::cerr << "Unknown option" << std::endl;
+void printUnknownOption(const std::string &execName, const std::string &option) {
+    std::cerr << "Unknown option: " << option << std::endl;
     printHelp(execName);
 }
 
-void printNoOptions(const std::string &execName) {
-    std::cerr << "No options given" << std::endl;
+void printInvalidParam(const std::string &execName, const std::string &param) {
+    std::cerr << "Invalid param: " << param << std::endl;
     printHelp(execName);
 }
 
+void printMissingArgument(const std::string &execName, const std::string &option) {
+    std::cerr << "Missing argument for option: " << option << std::endl;
+    printHelp(execName);
+}
+
+mode_t getMask(const char *mask) {
+    mode_t ret = static_cast<mode_t>(-1);
+    if (!mask)
+        return ret;
+    try {
+        ret = static_cast<mode_t>(std::stoi(mask, 0, 0));
+    } catch (...) {
+    }
+    return ret;
+}
+
+uid_t getUid(const char *user) {
+    uid_t ret = static_cast<uid_t>(-1);
+    if (!user)
+        return ret;
+    try {
+        ret = static_cast<uid_t>(std::stoi(user));
+    } catch (...) {
+        struct passwd *pwd = getpwnam(user);
+        if (pwd)
+            ret = pwd->pw_uid;
+    }
+    return ret;
+}
+
+gid_t getGid(const char *group) {
+    gid_t ret = static_cast<gid_t>(-1);
+    if (!group)
+        return ret;
+    try {
+        ret = static_cast<gid_t>(std::stoi(group));
+    } catch (...) {
+        struct group *grp = getgrnam(group);
+        if (grp)
+            ret = grp->gr_gid;
+    }
+    return ret;
+}
+
 } /* namespace CmdlineOpts */
 
 } /* namespace Cynara */
index 31d87df..e2c0ab1 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <ostream>
 #include <string>
+#include <sys/types.h>
 
 namespace Cynara {
 
@@ -32,16 +33,34 @@ namespace CmdlineParser {
 
 enum CmdlineOpt {
     Help = 'h',
-    Version = 'V'
+    Version = 'V',
+    Daemon = 'd',
+    Mask = 'm',
+    User = 'u',
+    Group = 'g',
+};
+
+struct CmdLineOptions {
+    bool m_error;
+    bool m_exit;
+
+    bool m_daemon;
+    mode_t m_mask;
+    uid_t m_uid;
+    gid_t m_gid;
 };
 
 std::ostream &operator<<(std::ostream &os, CmdlineOpt opt);
 
-bool handleCmdlineOptions(int argc, char * const *argv);
+struct CmdLineOptions handleCmdlineOptions(int argc, char * const *argv);
 void printHelp(const std::string &execName);
 void printVersion(void);
-void printUnknownOption(const std::string &execName);
-void printNoOptions(const std::string &execName);
+void printUnknownOption(const std::string &execName, const std::string &option);
+void printInvalidParam(const std::string &execName, const std::string &param);
+void printMissingArgument(const std::string &execName, const std::string &option);
+mode_t getMask(const char *mask);
+uid_t getUid(const char *user);
+gid_t getGid(const char *group);
 
 } /* namespace CmdlineOpts */
 
index 47d8414..58695be 100644 (file)
 #include "Cynara.h"
 
 int main(int argc, char **argv) {
-    init_log();
-
     try {
-        if (1 < argc) {
-            auto handlingSuccess = Cynara::CmdlineParser::handleCmdlineOptions(argc, argv);
-            return (handlingSuccess ? EXIT_SUCCESS : EXIT_FAILURE);
-        }
+        Cynara::CmdlineParser::CmdLineOptions options
+            = Cynara::CmdlineParser::handleCmdlineOptions(argc, argv);
+        if (options.m_error)
+            return EXIT_FAILURE;
+        if (options.m_exit)
+            return EXIT_SUCCESS;
+
+        init_log();
+
+        //todo use other options
 
         Cynara::Cynara cynara;
         LOGI("Cynara service is starting ...");
index 3f6a43b..1869a68 100644 (file)
 namespace {
 
 const std::string execName("./cynara");
-const std::string helpMessage("Usage: " + execName + " [OPTIONS]\n\n"
-        "  -V, --version                  print version of ./cynara and exit\n"
-        "  -h, --help                     print this help message and exit\n");
+const std::string helpMessage(
+    "Usage: " + execName + " [OPTIONS]\n\n"
+    "Information mode options [program exits after printing information]:\n"
+    "  -V, --version                print version of " + execName + " and exit\n"
+    "  -h, --help                   print this help message and exit\n"
+    "Normal work mode options:\n"
+    "  -d, --daemon                 daemonize "
+    "[by default " + execName + " does not daemonize]\n"
+    "  -m, --mask=MASK              set umask to MASK "
+    "[by default no umask is set]\n"
+    "  -u, --user=USER              change user to USER "
+                 "[by default uid is not changed]\n"
+    "  -g, --group=GROUP            change group to GROUP "
+                 "[by default gid is not changed]\n");
 
 } // namespace
 
@@ -53,10 +64,15 @@ TEST_F(CynaraCommandlineTest, help) {
         prepare_argv({ execName, opt });
 
         SCOPED_TRACE(opt);
-        const auto handlingSuccess = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
         getOutput(out, err);
 
-        ASSERT_TRUE(handlingSuccess);
+        ASSERT_FALSE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
         ASSERT_EQ(helpMessage, out);
         ASSERT_TRUE(err.empty());
     }
@@ -78,10 +94,15 @@ TEST_F(CynaraCommandlineTest, version) {
         prepare_argv({ execName, opt });
 
         SCOPED_TRACE(opt);
-        const auto handlingSuccess = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
         getOutput(out, err);
 
-        ASSERT_TRUE(handlingSuccess);
+        ASSERT_FALSE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
         ASSERT_EQ(std::string(CYNARA_VERSION) + "\n", out);
         ASSERT_TRUE(err.empty());
     }
@@ -103,21 +124,26 @@ TEST_F(CynaraCommandlineTest, unknownOption) {
         prepare_argv({ execName, badOpt });
 
         SCOPED_TRACE(badOpt);
-        const auto handlingSuccess = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
         getOutput(out, err);
 
-        ASSERT_FALSE(handlingSuccess);
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
         ASSERT_EQ(helpMessage, out);
-        ASSERT_EQ("Unknown option\n", err);
+        ASSERT_EQ(std::string("Unknown option: ") + badOpt + "\n", err);
     }
 }
 
 /**
- * @brief   Verify if passing no options to commandline handler returns error message
+ * @brief   Verify if passing no options to commandline handler succeeds
  * @test    Expected result:
- * - call handler indicates failure
- * - help message in output stream
- * - error message in error stream
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
  */
 TEST_F(CynaraCommandlineTest, noOption) {
     std::string err;
@@ -126,10 +152,391 @@ TEST_F(CynaraCommandlineTest, noOption) {
     clearOutput();
     prepare_argv({ execName });
 
-    const auto handlingSuccess = Parser::handleCmdlineOptions(this->argc(), this->argv());
+    const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
     getOutput(out, err);
 
-    ASSERT_FALSE(handlingSuccess);
-    ASSERT_EQ(helpMessage, out);
-    ASSERT_EQ("No options given\n", err);
+    ASSERT_FALSE(options.m_error);
+    ASSERT_FALSE(options.m_exit);
+    ASSERT_FALSE(options.m_daemon);
+    ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+    ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+    ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+    ASSERT_TRUE(out.empty());
+    ASSERT_TRUE(err.empty());
+}
+
+/**
+ * @brief   Verify if passing daemon option to commandline succeeds
+ * @test    Expected result:
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
+ */
+TEST_F(CynaraCommandlineTest, daemonOption) {
+    std::string err;
+    std::string out;
+
+    for (const auto &daemonOpt : { "-d", "--daemon" }) {
+        clearOutput();
+        prepare_argv({ execName, daemonOpt });
+
+        SCOPED_TRACE(daemonOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_FALSE(options.m_error);
+        ASSERT_FALSE(options.m_exit);
+        ASSERT_TRUE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_TRUE(out.empty());
+        ASSERT_TRUE(err.empty());
+    }
+}
+
+/**
+ * @brief   Verify if passing mask option to commandline succeeds
+ * @test    Expected result:
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
+ */
+TEST_F(CynaraCommandlineTest, maskOption) {
+    std::string err;
+    std::string out;
+
+    std::string maskParam("0666");
+
+    for (const auto &maskOpt : { "-m", "--mask" }) {
+        clearOutput();
+        prepare_argv({ execName, maskOpt, maskParam});
+
+        SCOPED_TRACE(maskOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_FALSE(options.m_error);
+        ASSERT_FALSE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, 0666);
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_TRUE(out.empty());
+        ASSERT_TRUE(err.empty());
+    }
+}
+
+/**
+ * @brief   Verify if passing invalid mask option to commandline fails
+ * @test    Expected result:
+ * - call handler indicates failure
+ * - help message in output stream
+ * - error message in error stream
+ */
+TEST_F(CynaraCommandlineTest, maskOptionInvalid) {
+    std::string err;
+    std::string out;
+
+    std::string maskParam("MASK");
+
+    for (const auto &maskOpt : { "-m", "--mask" }) {
+        clearOutput();
+        prepare_argv({ execName, maskOpt, maskParam});
+
+        SCOPED_TRACE(maskOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_EQ(helpMessage, out);
+        ASSERT_EQ("Invalid param: MASK\n", err);
+    }
+}
+
+/**
+ * @brief   Verify if passing no mask option to commandline fails
+ * @test    Expected result:
+ * - call handler indicates failure
+ * - help message in output stream
+ * - error message in error stream
+ */
+TEST_F(CynaraCommandlineTest, maskOptionNoParam) {
+    std::string err;
+    std::string out;
+
+    for (const auto &maskOpt : { "-m", "--mask" }) {
+        clearOutput();
+        prepare_argv({ execName, maskOpt});
+
+        SCOPED_TRACE(maskOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_EQ(helpMessage, out);
+        ASSERT_EQ(std::string("Missing argument for option: ") + maskOpt + "\n", err);
+    }
+}
+
+/**
+ * @brief   Verify if passing user option to commandline succeeds
+ * @test    Expected result:
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
+ */
+TEST_F(CynaraCommandlineTest, userOptionName) {
+    std::string err;
+    std::string out;
+
+    std::string userParam("root");
+
+    for (const auto &userOpt : { "-u", "--user" }) {
+        clearOutput();
+        prepare_argv({ execName, userOpt, userParam});
+
+        SCOPED_TRACE(userOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_FALSE(options.m_error);
+        ASSERT_FALSE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, 0);
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_TRUE(out.empty());
+        ASSERT_TRUE(err.empty());
+    }
+}
+
+/**
+ * @brief   Verify if passing user option to commandline succeeds
+ * @test    Expected result:
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
+ */
+TEST_F(CynaraCommandlineTest, userOptionUid) {
+    std::string err;
+    std::string out;
+
+    std::string userParam("1234");
+
+    for (const auto &userOpt : { "-u", "--user" }) {
+        clearOutput();
+        prepare_argv({ execName, userOpt, userParam});
+
+        SCOPED_TRACE(userOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_FALSE(options.m_error);
+        ASSERT_FALSE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, 1234);
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_TRUE(out.empty());
+        ASSERT_TRUE(err.empty());
+    }
+}
+
+/**
+ * @brief   Verify if passing invalid user option to commandline fails
+ * @test    Expected result:
+ * - call handler indicates success
+ * - help message in output stream
+ * - error message in error stream
+ */
+TEST_F(CynaraCommandlineTest, userOptionInvalid) {
+    std::string err;
+    std::string out;
+
+    std::string userParam("UserThatDoNotExist");
+
+    for (const auto &userOpt : { "-u", "--user" }) {
+        clearOutput();
+        prepare_argv({ execName, userOpt, userParam});
+
+        SCOPED_TRACE(userOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_EQ(helpMessage, out);
+        ASSERT_EQ("Invalid param: UserThatDoNotExist\n", err);
+    }
+}
+
+/**
+ * @brief   Verify if passing no user option to commandline fails
+ * @test    Expected result:
+ * - call handler indicates failure
+ * - help message in output stream
+ * - error message in error stream
+ */
+TEST_F(CynaraCommandlineTest, userOptionNoParam) {
+    std::string err;
+    std::string out;
+
+    for (const auto &userOpt : { "-u", "--user" }) {
+        clearOutput();
+        prepare_argv({ execName, userOpt});
+
+        SCOPED_TRACE(userOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_EQ(helpMessage, out);
+        ASSERT_EQ(std::string("Missing argument for option: ") + userOpt + "\n", err);
+    }
+}
+
+/**
+ * @brief   Verify if passing group option to commandline succeeds
+ * @test    Expected result:
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
+ */
+TEST_F(CynaraCommandlineTest, groupOptionName) {
+    std::string err;
+    std::string out;
+
+    std::string groupParam("root");
+
+    for (const auto &groupOpt : { "-g", "--group" }) {
+        clearOutput();
+        prepare_argv({ execName, groupOpt, groupParam});
+
+        SCOPED_TRACE(groupOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_FALSE(options.m_error);
+        ASSERT_FALSE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, 0);
+        ASSERT_TRUE(out.empty());
+        ASSERT_TRUE(err.empty());
+    }
+}
+
+/**
+ * @brief   Verify if passing group option to commandline succeeds
+ * @test    Expected result:
+ * - call handler indicates success
+ * - empty output stream
+ * - empty error stream
+ */
+TEST_F(CynaraCommandlineTest, groupOptionGid) {
+    std::string err;
+    std::string out;
+
+    std::string groupParam("1234");
+
+    for (const auto &groupOpt : { "-g", "--group" }) {
+        clearOutput();
+        prepare_argv({ execName, groupOpt, groupParam});
+
+        SCOPED_TRACE(groupOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_FALSE(options.m_error);
+        ASSERT_FALSE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, 1234);
+        ASSERT_TRUE(out.empty());
+        ASSERT_TRUE(err.empty());
+    }
+}
+
+/**
+ * @brief   Verify if passing invalid group option to commandline fails
+ * @test    Expected result:
+ * - call handler indicates success
+ * - help message in output stream
+ * - error message in error stream
+ */
+TEST_F(CynaraCommandlineTest, groupOptionInvalid) {
+    std::string err;
+    std::string out;
+
+    std::string groupParam("GroupThatDoNotExist");
+
+    for (const auto &groupOpt : { "-g", "--group" }) {
+        clearOutput();
+        prepare_argv({ execName, groupOpt, groupParam});
+
+        SCOPED_TRACE(groupOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_EQ(helpMessage, out);
+        ASSERT_EQ("Invalid param: GroupThatDoNotExist\n", err);
+    }
+}
+
+/**
+ * @brief   Verify if passing no group option to commandline fails
+ * @test    Expected result:
+ * - call handler indicates failure
+ * - help message in output stream
+ * - error message in error stream
+ */
+TEST_F(CynaraCommandlineTest, groupOptionNoParam) {
+    std::string err;
+    std::string out;
+
+    for (const auto &groupOpt : { "-g", "--group" }) {
+        clearOutput();
+        prepare_argv({ execName, groupOpt});
+
+        SCOPED_TRACE(groupOpt);
+        const auto options = Parser::handleCmdlineOptions(this->argc(), this->argv());
+        getOutput(out, err);
+
+        ASSERT_TRUE(options.m_error);
+        ASSERT_TRUE(options.m_exit);
+        ASSERT_FALSE(options.m_daemon);
+        ASSERT_EQ(options.m_mask, static_cast<mode_t>(-1));
+        ASSERT_EQ(options.m_uid, static_cast<uid_t>(-1));
+        ASSERT_EQ(options.m_gid, static_cast<gid_t>(-1));
+        ASSERT_EQ(helpMessage, out);
+        ASSERT_EQ(std::string("Missing argument for option: ") + groupOpt + "\n", err);
+    }
 }