diff --git a/iceoryx_examples/iceperf/roudi_main_static_config.cpp b/iceoryx_examples/iceperf/roudi_main_static_config.cpp index 36b7e7326a..a8a4e6820f 100644 --- a/iceoryx_examples/iceperf/roudi_main_static_config.cpp +++ b/iceoryx_examples/iceperf/roudi_main_static_config.cpp @@ -25,7 +25,12 @@ int main(int argc, char* argv[]) static constexpr uint32_t ONE_MEGABYTE = 1024U * 1024; iox::config::CmdLineParserConfigFileOption cmdLineParser; - cmdLineParser.parse(argc, argv); + auto cmdLineArgs = cmdLineParser.parse(argc, argv); + if (cmdLineArgs.has_error() && (cmdLineArgs.get_error() != iox::config::CmdLineParserResult::INFO_OUTPUT_ONLY)) + { + iox::LogFatal() << "Unable to parse command line arguments!"; + return EXIT_FAILURE; + } iox::RouDiConfig_t roudiConfig; // roudiConfig.setDefaults(); can be used if you want to use the default config only. @@ -61,9 +66,7 @@ int main(int argc, char* argv[]) /// mempoolConfig}) /// @endcode - cmdLineParser.printParameters(); - - IceOryxRouDiApp roudi(cmdLineParser, roudiConfig); + IceOryxRouDiApp roudi(cmdLineArgs.value(), roudiConfig); return roudi.run(); } diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp new file mode 100644 index 0000000000..874d037a3a --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/roudi/cmd_line_args.hpp @@ -0,0 +1,59 @@ +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// +// 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. +#ifndef IOX_POSH_ROUDI_CMD_LINE_ARGS_HPP +#define IOX_POSH_ROUDI_CMD_LINE_ARGS_HPP + +#include "iceoryx_posh/iceoryx_posh_types.hpp" +#include "iceoryx_posh/version/compatibility_check_level.hpp" +#include "iceoryx_utils/log/logstream.hpp" + +#include + +namespace iox +{ +namespace config +{ +struct CmdLineArgs_t +{ + roudi::MonitoringMode monitoringMode{roudi::MonitoringMode::ON}; + iox::log::LogLevel logLevel{iox::log::LogLevel::kWarn}; + version::CompatibilityCheckLevel compatibilityCheckLevel{version::CompatibilityCheckLevel::PATCH}; + units::Duration processKillDelay{roudi::PROCESS_DEFAULT_KILL_DELAY}; + cxx::optional uniqueRouDiId{cxx::nullopt}; + bool run{true}; + roudi::ConfigFilePathString_t configFilePath; +}; + +inline iox::log::LogStream& operator<<(iox::log::LogStream& logstream, const CmdLineArgs_t& cmdLineArgs) noexcept +{ + logstream << "Log level: " << cmdLineArgs.logLevel << "\n"; + logstream << "Monitoring mode: " << cmdLineArgs.monitoringMode << "\n"; + logstream << "Compatibility check level: " << cmdLineArgs.compatibilityCheckLevel << "\n"; + cmdLineArgs.uniqueRouDiId.and_then([&logstream](auto& id) { logstream << "Unique RouDi ID: " << id << "\n"; }) + .or_else([&logstream] { logstream << "Unique RouDi ID: < unset >\n"; }); + logstream << "Process kill delay: " << cmdLineArgs.processKillDelay.seconds() << " s\n"; + if (!cmdLineArgs.configFilePath.empty()) + { + logstream << "Config file used is: " << cmdLineArgs.configFilePath; + } + else + { + logstream << "Config file used is: < none >"; + } + return logstream; +} +} // namespace config +} // namespace iox + +#endif // IOX_POSH_ROUDI_CMD_LINE_ARGS_HPP diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/iceoryx_roudi_app.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/iceoryx_roudi_app.hpp index 45b2d9cc45..ca86823c1a 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/iceoryx_roudi_app.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/iceoryx_roudi_app.hpp @@ -26,7 +26,7 @@ class IceOryxRouDiApp : public RouDiApp /// @brief constructor to create the RouDi daemon with a given config /// @param[in] Command liner parser object, that provides the settings /// @param[in] RouDi config for mempool configuration - IceOryxRouDiApp(const config::CmdLineParser& cmdLineParser, const RouDiConfig_t& roudiConfig) noexcept; + IceOryxRouDiApp(const config::CmdLineArgs_t& cmdLineArgs, const RouDiConfig_t& roudiConfig) noexcept; /// @brief starts the execution of the RouDi daemon /// @return Return code for programm execution diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp index e5dad4a5ad..b9633dad55 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_app.hpp @@ -16,9 +16,7 @@ #include "iceoryx_posh/iceoryx_posh_config.hpp" #include "iceoryx_posh/mepoo/mepoo_config.hpp" -#include "iceoryx_posh/roudi/roudi_cmd_line_parser.hpp" -#include "iceoryx_posh/roudi/roudi_config_file_provider.hpp" -#include "iceoryx_posh/version/compatibility_check_level.hpp" +#include "iceoryx_posh/roudi/cmd_line_args.hpp" #include "iceoryx_utils/log/logcommon.hpp" #include "iceoryx_utils/posix_wrapper/semaphore.hpp" @@ -39,7 +37,7 @@ class RouDiApp /// @brief C'tor with command line parser, which has already parsed the command line parameters /// @param[in] cmdLineParser reference to a command line parser object /// @param[in] config the configuration to use - RouDiApp(const config::CmdLineParser& cmdLineParser, const RouDiConfig_t& config) noexcept; + RouDiApp(const config::CmdLineArgs_t& cmdLineArgs, const RouDiConfig_t& config) noexcept; virtual ~RouDiApp() noexcept {}; @@ -48,24 +46,9 @@ class RouDiApp virtual uint8_t run() noexcept = 0; protected: - /// @brief this is needed for the child classes for custom CmdLineParser - /// @param[in] config the configuration to use - RouDiApp(const RouDiConfig_t& config) noexcept; - /// @brief Tells the OS which signals shall be hooked void registerSigHandler() noexcept; - void parseCmdLineArguments(int argc, - char* argv[], - config::CmdLineParser::CmdLineArgumentParsingMode cmdLineParsingMode = - config::CmdLineParser::CmdLineArgumentParsingMode::ALL) noexcept; - - /// @brief Extracts from CmdLineParser and sets them - void setCmdLineParserResults(const config::CmdLineParser& cmdLineParser) noexcept; - - /// @brief initialize the RouDi daemon - void init() noexcept; - /// @brief waits for the next signal to RouDi daemon bool waitForSignal() const noexcept; diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp index 058c6990b6..d10087acb5 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser.hpp @@ -15,6 +15,7 @@ #define IOX_POSH_ROUDI_ROUDI_CMD_LINE_PARSER_HPP #include "iceoryx_posh/iceoryx_posh_types.hpp" +#include "iceoryx_posh/roudi/cmd_line_args.hpp" #include "iceoryx_posh/version/compatibility_check_level.hpp" #include "iceoryx_utils/cxx/expected.hpp" #include "iceoryx_utils/cxx/optional.hpp" @@ -25,6 +26,12 @@ namespace iox { namespace config { +enum class CmdLineParserResult +{ + UNKNOWN_OPTION_USED, + INFO_OUTPUT_ONLY /// @todo use this instead of CmdLineArgs_t.run after modularisation of RouDi +}; + class CmdLineParser { public: @@ -45,18 +52,12 @@ class CmdLineParser /// @param[in] argc forwarding of command line arguments /// @param[in] argv forwarding of command line arguments /// @param[in] cmdLineParsingMode selects to parse a single option or all options - virtual void parse(int argc, - char* argv[], - const CmdLineArgumentParsingMode cmdLineParsingMode = CmdLineArgumentParsingMode::ALL) noexcept; - - void printParameters() const noexcept; - - bool getRun() const noexcept; - iox::log::LogLevel getLogLevel() const noexcept; - roudi::MonitoringMode getMonitoringMode() const noexcept; - version::CompatibilityCheckLevel getCompatibilityCheckLevel() const noexcept; - cxx::optional getUniqueRouDiId() const noexcept; - units::Duration getProcessKillDelay() const noexcept; + /// @param[out] Result wrapped in an cxx::expected, either the parsed arguments as CmdLineArgs_t struct or + /// CmdLineParserResult + virtual cxx::expected + parse(int argc, + char* argv[], + const CmdLineArgumentParsingMode cmdLineParsingMode = CmdLineArgumentParsingMode::ALL) noexcept; protected: bool m_run{true}; diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp index 2d81553fe1..bb6da90426 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp @@ -34,13 +34,12 @@ class CmdLineParserConfigFileOption : public CmdLineParser /// @param[in] argc forwarding of command line arguments /// @param[in] argv forwarding of command line arguments /// @param[in] cmdLineParsingMode selects to parse a single option or all options - void parse(int argc, - char* argv[], - const CmdLineArgumentParsingMode cmdLineParsingMode = CmdLineArgumentParsingMode::ALL) noexcept override; - - roudi::ConfigFilePathString_t getConfigFilePath() const; - - void printParameters() noexcept; + /// @param[out] Result wrapped in an cxx::expected, either the parsed arguments as CmdLineArgs_t struct or + /// CmdLineParserResult + cxx::expected + parse(int argc, + char* argv[], + const CmdLineArgumentParsingMode cmdLineParsingMode = CmdLineArgumentParsingMode::ALL) noexcept override; protected: roudi::ConfigFilePathString_t m_customConfigFilePath; diff --git a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config_toml_file_provider.hpp b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config_toml_file_provider.hpp index d8e13b5a01..ab27e1127e 100644 --- a/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config_toml_file_provider.hpp +++ b/iceoryx_posh/include/iceoryx_posh/roudi/roudi_config_toml_file_provider.hpp @@ -14,7 +14,7 @@ #ifndef IOX_POSH_ROUDI_ROUDI_CONFIG_TOML_FILE_PROVIDER_HPP #define IOX_POSH_ROUDI_ROUDI_CONFIG_TOML_FILE_PROVIDER_HPP -#include "iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp" +#include "iceoryx_posh/roudi/cmd_line_args.hpp" #include "iceoryx_posh/roudi/roudi_config_file_provider.hpp" namespace iox @@ -26,7 +26,7 @@ static constexpr char defaultConfigFilePath[] = "/etc/iceoryx/roudi_config.toml" class TomlRouDiConfigFileProvider : public iox::roudi::RouDiConfigFileProvider { public: - TomlRouDiConfigFileProvider(CmdLineParserConfigFileOption& cmdLineParserValue); + TomlRouDiConfigFileProvider(iox::config::CmdLineArgs_t& cmdLineArgs); iox::cxx::expected parse() override; }; diff --git a/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp b/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp index c4c2b536b0..afd3b206bd 100644 --- a/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp +++ b/iceoryx_posh/source/roudi/application/iceoryx_roudi_app.cpp @@ -23,8 +23,8 @@ namespace iox { namespace roudi { -IceOryxRouDiApp::IceOryxRouDiApp(const config::CmdLineParser& cmdLineParser, const RouDiConfig_t& roudiConfig) noexcept - : RouDiApp(cmdLineParser, roudiConfig) +IceOryxRouDiApp::IceOryxRouDiApp(const config::CmdLineArgs_t& cmdLineArgs, const RouDiConfig_t& roudiConfig) noexcept + : RouDiApp(cmdLineArgs, roudiConfig) { } diff --git a/iceoryx_posh/source/roudi/application/roudi_app.cpp b/iceoryx_posh/source/roudi/application/roudi_app.cpp index fae76e9418..32f0c73157 100644 --- a/iceoryx_posh/source/roudi/application/roudi_app.cpp +++ b/iceoryx_posh/source/roudi/application/roudi_app.cpp @@ -18,6 +18,7 @@ #include "iceoryx_posh/internal/log/posh_logging.hpp" #include "iceoryx_posh/internal/popo/building_blocks/typed_unique_id.hpp" #include "iceoryx_posh/internal/roudi/roudi.hpp" +#include "iceoryx_posh/roudi/cmd_line_args.hpp" #include "iceoryx_utils/cxx/helplets.hpp" #include "iceoryx_utils/cxx/optional.hpp" #include "iceoryx_utils/internal/posix_wrapper/shared_memory_object/memory_map.hpp" @@ -90,17 +91,30 @@ void RouDiApp::registerSigHandler() noexcept } } -RouDiApp::RouDiApp(const config::CmdLineParser& cmdLineParser, const RouDiConfig_t& config) noexcept - : RouDiApp(config) -{ - setCmdLineParserResults(cmdLineParser); - init(); -} - -RouDiApp::RouDiApp(const RouDiConfig_t& config) noexcept - : m_run(checkAndOptimizeConfig(config)) +RouDiApp::RouDiApp(const config::CmdLineArgs_t& cmdLineArgs, const RouDiConfig_t& config) noexcept + : m_logLevel(cmdLineArgs.logLevel) + , m_monitoringMode(cmdLineArgs.monitoringMode) + , m_run(checkAndOptimizeConfig(config)) , m_config(config) + , m_compatibilityCheckLevel(cmdLineArgs.compatibilityCheckLevel) + , m_processKillDelay(cmdLineArgs.processKillDelay) { + // the "and" is intentional, just in case the the provided RouDiConfig_t is empty + m_run &= cmdLineArgs.run; + if (cmdLineArgs.uniqueRouDiId) + { + popo::internal::setUniqueRouDiId(cmdLineArgs.uniqueRouDiId.value()); + } + + // be silent if not running + if (m_run) + { + iox::log::LogManager::GetLogManager().SetDefaultLogLevel(m_logLevel); + + registerSigHandler(); + + LogVerbose() << "Command line parameters are:\n" << cmdLineArgs; + } } bool RouDiApp::checkAndOptimizeConfig(const RouDiConfig_t& config) noexcept @@ -123,37 +137,10 @@ bool RouDiApp::checkAndOptimizeConfig(const RouDiConfig_t& config) noexcept return true; } -void RouDiApp::init() noexcept -{ - // be silent if not running - if (m_run) - { - iox::log::LogManager::GetLogManager().SetDefaultLogLevel(m_logLevel); - - registerSigHandler(); - } -} - bool RouDiApp::waitForSignal() const noexcept { return !m_semaphore.wait().has_error(); } -void RouDiApp::setCmdLineParserResults(const config::CmdLineParser& cmdLineParser) noexcept -{ - m_monitoringMode = cmdLineParser.getMonitoringMode(); - m_logLevel = cmdLineParser.getLogLevel(); - // the "and" is intentional, just in case the the provided RouDiConfig_t is empty - m_run &= cmdLineParser.getRun(); - m_compatibilityCheckLevel = cmdLineParser.getCompatibilityCheckLevel(); - m_processKillDelay = cmdLineParser.getProcessKillDelay(); - auto uniqueId = cmdLineParser.getUniqueRouDiId(); - if (uniqueId) - { - popo::internal::setUniqueRouDiId(*uniqueId); - } -} - - } // namespace roudi } // namespace iox diff --git a/iceoryx_posh/source/roudi/application/roudi_main.cpp b/iceoryx_posh/source/roudi/application/roudi_main.cpp index 10f0bc32c2..67cdf8d8f4 100644 --- a/iceoryx_posh/source/roudi/application/roudi_main.cpp +++ b/iceoryx_posh/source/roudi/application/roudi_main.cpp @@ -15,6 +15,7 @@ #include "iceoryx_posh/iceoryx_posh_config.hpp" #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/log/posh_logging.hpp" +#include "iceoryx_posh/roudi/cmd_line_args.hpp" #include "iceoryx_posh/roudi/iceoryx_roudi_app.hpp" #include "iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp" #include "iceoryx_posh/roudi/roudi_config_toml_file_provider.hpp" @@ -24,23 +25,25 @@ int main(int argc, char* argv[]) using iox::roudi::IceOryxRouDiApp; iox::config::CmdLineParserConfigFileOption cmdLineParser; - cmdLineParser.parse(argc, argv); - - iox::config::TomlRouDiConfigFileProvider configFileProvider(cmdLineParser); - - iox::RouDiConfig_t roudiConfig = - configFileProvider.parse() - .or_else([](iox::roudi::RouDiConfigFileParseError& parseResult) { - iox::LogFatal() << "Couldn't parse config file. Error: " - << iox::cxx::convertEnumToString(iox::roudi::ROUDI_CONFIG_FILE_PARSE_ERROR_STRINGS, - parseResult); - std::terminate(); - }) - .value(); - - cmdLineParser.printParameters(); - - IceOryxRouDiApp roudi(cmdLineParser, roudiConfig); + auto cmdLineArgs = cmdLineParser.parse(argc, argv); + if (cmdLineArgs.has_error() && (cmdLineArgs.get_error() != iox::config::CmdLineParserResult::INFO_OUTPUT_ONLY)) + { + iox::LogFatal() << "Unable to parse command line arguments!"; + return EXIT_FAILURE; + } + + iox::config::TomlRouDiConfigFileProvider configFileProvider(cmdLineArgs.value()); + + auto roudiConfig = configFileProvider.parse(); + if (roudiConfig.has_error()) + { + iox::LogFatal() << "Couldn't parse config file. Error: " + << iox::cxx::convertEnumToString(iox::roudi::ROUDI_CONFIG_FILE_PARSE_ERROR_STRINGS, + roudiConfig.get_error()); + return EXIT_FAILURE; + } + + IceOryxRouDiApp roudi(cmdLineArgs.value(), roudiConfig.value()); return roudi.run(); } diff --git a/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp b/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp index 06c53a3db1..29e972d4d2 100644 --- a/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp +++ b/iceoryx_posh/source/roudi/roudi_cmd_line_parser.cpp @@ -24,20 +24,20 @@ namespace iox { namespace config { -void CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cmdLineParsingMode) noexcept +cxx::expected +CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMode cmdLineParsingMode) noexcept { constexpr option longOptions[] = {{"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, {"monitoring-mode", required_argument, nullptr, 'm'}, {"log-level", required_argument, nullptr, 'l'}, - {"ignore-version", required_argument, nullptr, 'i'}, {"unique-roudi-id", required_argument, nullptr, 'u'}, - {"compatibility", required_argument, nullptr, 'c'}, + {"compatibility", required_argument, nullptr, 'x'}, {"kill-delay", required_argument, nullptr, 'k'}, {nullptr, 0, nullptr, 0}}; // colon after shortOption means it requires an argument, two colons mean optional argument - constexpr const char* shortOptions = "hvm:l:u:c:k:"; + constexpr const char* shortOptions = "hvm:l:u:x:k:"; int32_t index; int32_t opt{-1}; while ((opt = getopt_long(argc, argv, shortOptions, longOptions, &index), opt != -1)) @@ -58,7 +58,7 @@ void CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMo std::cout << "-l, --log-level Set log level." << std::endl; std::cout << " {off, fatal, error, warning, info, debug, verbose}" << std::endl; - std::cout << "-c, --compatibility Set compatibility check level between runtime and RouDi." + std::cout << "-x, --compatibility Set compatibility check level between runtime and RouDi." << std::endl; std::cout << " off: no check" << std::endl; std::cout << " major: same major version " << std::endl; @@ -201,6 +201,7 @@ void CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMo { // CmdLineParser did not understand the parameters, don't run m_run = false; + return cxx::error(CmdLineParserResult::UNKNOWN_OPTION_USED); } }; @@ -209,46 +210,10 @@ void CmdLineParser::parse(int argc, char* argv[], const CmdLineArgumentParsingMo break; } } + return cxx::success(CmdLineArgs_t{ + m_monitoringMode, m_logLevel, m_compatibilityCheckLevel, m_processKillDelay, m_uniqueRouDiId, m_run, ""}); } // namespace roudi -bool CmdLineParser::getRun() const noexcept -{ - return m_run; -} -iox::log::LogLevel CmdLineParser::getLogLevel() const noexcept -{ - return m_logLevel; -} -roudi::MonitoringMode CmdLineParser::getMonitoringMode() const noexcept -{ - return m_monitoringMode; -} - -version::CompatibilityCheckLevel CmdLineParser::getCompatibilityCheckLevel() const noexcept -{ - return m_compatibilityCheckLevel; -} -cxx::optional CmdLineParser::getUniqueRouDiId() const noexcept -{ - return m_uniqueRouDiId; -} - -units::Duration CmdLineParser::getProcessKillDelay() const noexcept -{ - return m_processKillDelay; -} - -void CmdLineParser::printParameters() const noexcept -{ - LogVerbose() << "Command line parameters are.."; - LogVerbose() << "Log level: " << m_logLevel; - LogVerbose() << "Monitoring mode: " << m_monitoringMode; - LogVerbose() << "Compatibility check level: " << m_compatibilityCheckLevel; - m_uniqueRouDiId.and_then([](auto& id) { LogVerbose() << "Unique RouDi ID: " << id; }).or_else([] { - LogVerbose() << "Unique RouDi ID: < unset >"; - }); - LogVerbose() << "Process kill delay: " << m_processKillDelay.seconds() << " s"; -} } // namespace config } // namespace iox diff --git a/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp b/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp index 4583704e35..f4573c6274 100644 --- a/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp +++ b/iceoryx_posh/source/roudi/roudi_cmd_line_parser_config_file_option.cpp @@ -23,9 +23,8 @@ namespace iox { namespace config { -void CmdLineParserConfigFileOption::parse(int argc, - char* argv[], - const CmdLineArgumentParsingMode cmdLineParsingMode) noexcept +cxx::expected CmdLineParserConfigFileOption::parse( + int argc, char* argv[], const CmdLineArgumentParsingMode cmdLineParsingMode) noexcept { constexpr option longOptions[] = {{"help", no_argument, nullptr, 'h'}, {"config-file", required_argument, nullptr, 'c'}, @@ -63,7 +62,11 @@ void CmdLineParserConfigFileOption::parse(int argc, { // we want to parse the help option again, therefore we need to decrement the option index of getopt optind--; - CmdLineParser::parse(argc, argv, CmdLineArgumentParsingMode::ONE); + auto result = CmdLineParser::parse(argc, argv, CmdLineArgumentParsingMode::ONE); + if (result.has_error()) + { + return cxx::error(result.get_error()); + } } }; @@ -72,16 +75,13 @@ void CmdLineParserConfigFileOption::parse(int argc, break; } } -} -roudi::ConfigFilePathString_t CmdLineParserConfigFileOption::getConfigFilePath() const -{ - return m_customConfigFilePath; -} - -void CmdLineParserConfigFileOption::printParameters() noexcept -{ - CmdLineParser::printParameters(); - LogVerbose() << "Config file used is: " << m_customConfigFilePath; + return cxx::success(CmdLineArgs_t{m_monitoringMode, + m_logLevel, + m_compatibilityCheckLevel, + m_processKillDelay, + m_uniqueRouDiId, + m_run, + m_customConfigFilePath}); } } // namespace config diff --git a/iceoryx_posh/source/roudi/roudi_config_toml_file_provider.cpp b/iceoryx_posh/source/roudi/roudi_config_toml_file_provider.cpp index 8102e5da2b..7563bff776 100644 --- a/iceoryx_posh/source/roudi/roudi_config_toml_file_provider.cpp +++ b/iceoryx_posh/source/roudi/roudi_config_toml_file_provider.cpp @@ -28,12 +28,12 @@ namespace iox { namespace config { -TomlRouDiConfigFileProvider::TomlRouDiConfigFileProvider(CmdLineParserConfigFileOption& cmdLineParser) +TomlRouDiConfigFileProvider::TomlRouDiConfigFileProvider(config::CmdLineArgs_t& cmdLineArgs) { /// don't print additional output if not running - if (cmdLineParser.getRun()) + if (cmdLineArgs.run) { - if (cmdLineParser.getConfigFilePath().size() == 0) + if (cmdLineArgs.configFilePath.empty()) { /// @todo Replace with C++17 std::filesystem::exists() cxx::FileReader configFile(defaultConfigFilePath, "", cxx::FileReader::ErrorMode::Ignore); @@ -49,14 +49,14 @@ TomlRouDiConfigFileProvider::TomlRouDiConfigFileProvider(CmdLineParserConfigFile << "'. Falling back to built-in config."; } } - m_customConfigFilePath = cmdLineParser.getConfigFilePath(); + m_customConfigFilePath = cmdLineArgs.configFilePath; } } iox::cxx::expected TomlRouDiConfigFileProvider::parse() { // Early exit in case no config file path was provided - if (m_customConfigFilePath.size() == 0) + if (m_customConfigFilePath.empty()) { iox::RouDiConfig_t defaultConfig; defaultConfig.setDefaults(); diff --git a/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp b/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp new file mode 100644 index 0000000000..2c629a3d41 --- /dev/null +++ b/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser.cpp @@ -0,0 +1,429 @@ +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// +// 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. + +#if !defined(_WIN32) +#include "test.hpp" + +#include "iceoryx_posh/roudi/roudi_cmd_line_parser.hpp" + +using namespace ::testing; +using ::testing::Return; + +using namespace iox::roudi; +using namespace iox::config; +using namespace iox::log; +using namespace iox::units; +using namespace iox::version; + +namespace iox +{ +namespace config +{ +bool operator==(const CmdLineArgs_t& lhs, const CmdLineArgs_t& rhs) +{ + return (lhs.monitoringMode == rhs.monitoringMode) && (lhs.logLevel == rhs.logLevel) + && (lhs.compatibilityCheckLevel == rhs.compatibilityCheckLevel) + && (lhs.processKillDelay == rhs.processKillDelay) && (lhs.uniqueRouDiId == rhs.uniqueRouDiId) + && (lhs.run == rhs.run) && (lhs.configFilePath == rhs.configFilePath); +} +} // namespace config +} // namespace iox + + +class CmdLineParser_test : public Test +{ + public: + void SetUp(){}; + void TearDown() + { + // Reset optind to be able to parse again + optind = 0; + }; + + void testLogLevel(uint8_t numberOfArgs, char* args[], LogLevel level) + { + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().logLevel, Eq(level)); + + // Reset optind to be able to parse again + optind = 0; + } + + void testMonitoringMode(uint8_t numberOfArgs, char* args[], MonitoringMode mode) + { + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().monitoringMode, Eq(mode)); + + // Reset optind to be able to parse again + optind = 0; + } + + void testCompatibilityLevel(uint8_t numberOfArgs, char* args[], CompatibilityCheckLevel level) + { + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().compatibilityCheckLevel, Eq(level)); + + // Reset optind to be able to parse again + optind = 0; + } +}; + +TEST_F(CmdLineParser_test, NoOptionLeadsToDefaultValues) +{ + constexpr uint8_t numberOfArgs{1U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + args[0] = &appName[0]; + const CmdLineArgs_t defaultValues; + + CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value(), Eq(defaultValues)); +} + +TEST_F(CmdLineParser_test, WrongOptionLeadsUnkownOptionResult) +{ + constexpr uint8_t numberOfArgs{2U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--ICanHazLulz"; + args[0] = &appName[0]; + args[1] = &option[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(true)); + EXPECT_THAT(result.get_error(), Eq(CmdLineParserResult::UNKNOWN_OPTION_USED)); +} + +TEST_F(CmdLineParser_test, HelpLongOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{2U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--help"; + args[0] = &appName[0]; + args[1] = &option[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, HelpShortOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{2U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-h"; + args[0] = &appName[0]; + args[1] = &option[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, VersionShortOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{2U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-v"; + args[0] = &appName[0]; + args[1] = &option[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, VersionLongOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{2U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--version"; + args[0] = &appName[0]; + args[1] = &option[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, MonitoringModeOptionsLeadToCorrectMode) +{ + constexpr uint8_t numberOfArgs{3U}; + MonitoringMode modeArray[] = {MonitoringMode::ON, MonitoringMode::OFF}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char optionArray[][20] = {"-m", "--monitoring-mode"}; + char valueArray[][10] = {"on", "off"}; + args[0] = &appName[0]; + + for (auto optionValue : optionArray) + { + args[1] = optionValue; + uint8_t i{0U}; + for (auto expectedValue : modeArray) + { + args[2] = valueArray[i]; + testMonitoringMode(numberOfArgs, args, expectedValue); + i++; + } + } +} + +TEST_F(CmdLineParser_test, WrongMonitoringModeOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-m"; + char wrongValue[] = "DontBlink"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &wrongValue[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, LogLevelOptionsLeadToCorrectLogLevel) +{ + constexpr uint8_t numberOfArgs{3U}; + LogLevel loglevelArray[] = {LogLevel::kOff, + LogLevel::kFatal, + LogLevel::kError, + LogLevel::kWarn, + LogLevel::kInfo, + LogLevel::kDebug, + LogLevel::kVerbose}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char optionArray[][20] = {"-l", "--log-level"}; + char valueArray[][10] = {"off", "fatal", "error", "warn", "info", "debug", "verbose"}; + args[0] = &appName[0]; + + for (auto optionValue : optionArray) + { + args[1] = optionValue; + uint8_t i{0U}; + for (auto expectedValue : loglevelArray) + { + args[2] = valueArray[i]; + testLogLevel(numberOfArgs, args, expectedValue); + i++; + } + } +} + +TEST_F(CmdLineParser_test, WrongLogLevelOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-l"; + char wrongValue[] = "TimeyWimey"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &wrongValue[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, KillDelayLongOptionLeadsToCorrectDelay) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--kill-delay"; + char value[] = "73"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().processKillDelay, Eq(Duration::seconds(73))); +} + +TEST_F(CmdLineParser_test, KillDelayShortOptionLeadsToCorrectDelay) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-k"; + char value[] = "42"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().processKillDelay, Eq(Duration::seconds(42))); +} + +TEST_F(CmdLineParser_test, KillDelayOptionOutOfBoundsLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--kill-delay"; + char value[] = "4294967296"; // MAX_PROCESS_KILL_DELAY + 1 + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, CompatibilityLevelOptionsLeadToCorrectCompatibilityLevel) +{ + constexpr uint8_t numberOfArgs{3U}; + CompatibilityCheckLevel loglevelArray[] = {CompatibilityCheckLevel::OFF, + CompatibilityCheckLevel::MAJOR, + CompatibilityCheckLevel::MINOR, + CompatibilityCheckLevel::PATCH, + CompatibilityCheckLevel::COMMIT_ID, + CompatibilityCheckLevel::BUILD_DATE}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char optionArray[][20] = {"-x", "--compatibility"}; + char valueArray[][10] = {"off", "major", "minor", "patch", "commitId", "buildDate"}; + args[0] = &appName[0]; + + for (auto optionValue : optionArray) + { + args[1] = optionValue; + uint8_t i{0U}; + for (auto expectedValue : loglevelArray) + { + args[2] = valueArray[i]; + testCompatibilityLevel(numberOfArgs, args, expectedValue); + i++; + } + } +} + +TEST_F(CmdLineParser_test, WrongCompatibilityLevelOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-x"; + char wrongValue[] = "AmyPond"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &wrongValue[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} + +TEST_F(CmdLineParser_test, UniqueIdLongOptionLeadsToCorrectUniqueId) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--unique-roudi-id"; + char value[] = "4242"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().uniqueRouDiId.has_value(), Eq(true)); + EXPECT_THAT(result.value().uniqueRouDiId.value(), Eq(4242)); +} + +TEST_F(CmdLineParser_test, UniqueIdShortOptionLeadsToCorrectUniqueId) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-u"; + char value[] = "4242"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &value[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().uniqueRouDiId.has_value(), Eq(true)); + EXPECT_THAT(result.value().uniqueRouDiId.value(), Eq(4242)); +} + +TEST_F(CmdLineParser_test, OutOfBoundsUniqueIdOptionLeadsToProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-u"; + char wrongValue[] = "65536"; // MAX_ROUDI_ID + 1 + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &wrongValue[0]; + + iox::config::CmdLineParser sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} +#endif diff --git a/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser_config_file_option.cpp b/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser_config_file_option.cpp new file mode 100644 index 0000000000..9238bb03d0 --- /dev/null +++ b/iceoryx_posh/test/moduletests/test_roudi_cmd_line_parser_config_file_option.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. +// +// 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. + +#if !defined(_WIN32) +#include "test.hpp" + +#include "iceoryx_posh/roudi/roudi_cmd_line_parser_config_file_option.hpp" +#include "iceoryx_utils/cxx/string.hpp" + +#include + +using namespace ::testing; +using ::testing::Return; + +using namespace iox::cxx; + +class CmdLineParserConfigFileOption_test : public Test +{ + public: + void SetUp(){}; + void TearDown() + { + // Reset optind to be able to parse again + optind = 0; + }; +}; + +TEST_F(CmdLineParserConfigFileOption_test, NoConfigPathOptionLeadsToEmptyPath) +{ + constexpr uint8_t numberOfArgs{1U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + args[0] = &appName[0]; + + iox::config::CmdLineParserConfigFileOption sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().configFilePath.c_str(), StrEq("")); +} + +TEST_F(CmdLineParserConfigFileOption_test, ConfigPathShortOptionIsCorrectlyRead) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "-c"; + char path[] = "/foo/bar.toml"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &path[0]; + + iox::config::CmdLineParserConfigFileOption sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().configFilePath.c_str(), StrEq(path)); +} + +TEST_F(CmdLineParserConfigFileOption_test, ConfigPathLongOptionIsCorrectlyRead) +{ + constexpr uint8_t numberOfArgs{3U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--config-file"; + char path[] = "/foo/bar/baz.toml"; + args[0] = &appName[0]; + args[1] = &option[0]; + args[2] = &path[0]; + + iox::config::CmdLineParserConfigFileOption sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().configFilePath.c_str(), StrEq(path)); +} + +TEST_F(CmdLineParserConfigFileOption_test, HelpLongOptionLeadsProgrammNotRunning) +{ + constexpr uint8_t numberOfArgs{2U}; + char* args[numberOfArgs]; + char appName[] = "./foo"; + char option[] = "--help"; + args[0] = &appName[0]; + args[1] = &option[0]; + + iox::config::CmdLineParserConfigFileOption sut; + auto result = sut.parse(numberOfArgs, args); + + EXPECT_THAT(result.has_error(), Eq(false)); + EXPECT_THAT(result.value().run, Eq(false)); +} +#endif