Browse Source

Added `app_parameters` class and tests

master
Jessica James 5 years ago
parent
commit
27ddf01052
  1. 9
      src/common/CMakeLists.txt
  2. 136
      src/common/app_parameters.cpp
  3. 44
      src/include/app_parameters.hpp
  4. 2
      src/test/CMakeLists.txt
  5. 277
      src/test/app_parameters.cpp

9
src/common/CMakeLists.txt

@ -1,8 +1,6 @@
cmake_minimum_required(VERSION 3.8)
# Setup source files
set(SOURCE_FILES
timer/timer.cpp timer/timer_manager.cpp thread_pool.cpp timer/timer_context.cpp timer/cancel_token.cpp timer/synchronized_timer.cpp object.cpp parser/parser.cpp parser/parser_manager.cpp config.cpp serialize.cpp parsers/json.cpp unicode.cpp)
timer/timer.cpp timer/timer_manager.cpp thread_pool.cpp timer/timer_context.cpp timer/cancel_token.cpp timer/synchronized_timer.cpp object.cpp parser/parser.cpp parser/parser_manager.cpp config.cpp serialize.cpp parsers/json.cpp unicode.cpp app_parameters.cpp)
# Setup library build target
add_library(jessilib ${SOURCE_FILES})
@ -13,3 +11,8 @@ target_include_directories(jessilib PRIVATE ../include/impl/asio/include)
# Setup additionally needed libs
target_link_libraries(jessilib ${JESSILIB_ADDITOINAL_LIBS})
# Setup fmt::fmt; include directories are included as SYSTEM to suppress warnings (which would be treated as errors)
get_target_property(fmt_include_dirs fmt::fmt INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(jessilib SYSTEM PUBLIC ${fmt_include_dirs})
target_link_libraries(jessilib fmt::fmt)

136
src/common/app_parameters.cpp

@ -0,0 +1,136 @@
/**
* Copyright (C) 2019 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
*/
#include "app_parameters.hpp"
namespace jessilib {
app_parameters::app_parameters(int in_argc, char** in_argv)
: app_parameters{ in_argc, const_cast<const char**>(in_argv) } {
// Empty ctor body
}
app_parameters::app_parameters(int in_argc, const char** in_argv) {
// Sanity safety check; should never happen
if (in_argc <= 0 || in_argv == nullptr) {
return;
}
// Populate path
m_path = in_argv[0];
// Process args
std::string_view key{ nullptr };
std::string value;
auto flush_value = [&key, &value, this]() {
// This is the start of a key; flush what we were previously processing
if (!key.empty()) {
if (value.empty()) {
m_switches.emplace_back(key);
}
else {
m_values.emplace(key, std::move(value));
value.clear();
}
}
};
for (int index = 1; index < in_argc; ++index) {
const char* arg = in_argv[index];
if (arg != nullptr && *arg != '\0') {
// Check if this is a key or value
if (*arg == '-') {
// Flush pending value (if any)
flush_value();
// Strip any leading '-' or '--' and set key
key = arg + 1;
if (key[0] == '-') {
key.remove_prefix(1);
}
// Parse key for any value denominator ('=')
size_t key_end = key.find('=');
if (key_end != std::string_view::npos) {
// arg contains start of a value
value = key.substr(key_end + 1);
key = key.substr(0, key_end);
}
}
else {
// This is part of a value; add it
if (!value.empty()) {
value += ' ';
}
value += arg;
}
}
// else // empty string encountered
// Push arg
m_args.emplace_back(arg);
}
// Flush any pending switch/value
flush_value();
}
std::string_view app_parameters::path() const {
return m_path;
}
const std::vector<std::string_view>& app_parameters::arguments() const {
return m_args;
}
const std::vector<std::string_view>& app_parameters::switches() const {
return m_switches;
}
std::unordered_set<std::string_view> app_parameters::switches_set() const {
return { m_switches.begin(), m_switches.end() };
}
const std::unordered_map<std::string_view, std::string>& app_parameters::values() const {
return m_values;
}
object app_parameters::as_object() const {
// Null check
if (m_path.empty()
&& m_args.empty()) {
// app_parameters is null; return a null object
return object{};
}
// Transform m_values into appropriate map type; TODO: add helper to object for this
std::map<std::string, object> values_map;
for (auto& value : m_values) {
values_map.emplace(value.first, value.second);
}
return std::map<std::string, object>{
{ "Path", m_path },
{ "Args", m_args },
{ "Switches", m_switches },
{ "Values", values_map }
};
}
} // namespace jessilib

44
src/include/app_parameters.hpp

@ -0,0 +1,44 @@
/**
* Copyright (C) 2019 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
*/
#include "object.hpp"
namespace jessilib {
class app_parameters {
public:
app_parameters(int in_argc, char** in_argv);
app_parameters(int in_argc, const char** in_argv);
std::string_view path() const;
const std::vector<std::string_view>& arguments() const;
const std::vector<std::string_view>& switches() const;
std::unordered_set<std::string_view> switches_set() const;
const std::unordered_map<std::string_view, std::string>& values() const;
jessilib::object as_object() const;
operator jessilib::object() const { return as_object(); }
private:
std::string_view m_path;
std::vector<std::string_view> m_args;
std::vector<std::string_view> m_switches;
std::unordered_map<std::string_view, std::string> m_values;
};
} // namespace jessilib

2
src/test/CMakeLists.txt

@ -1,6 +1,6 @@
# Setup source files
set(SOURCE_FILES
timer.cpp thread_pool.cpp util.cpp object.cpp parser.cpp config.cpp parsers/json.cpp unicode.cpp)
timer.cpp thread_pool.cpp util.cpp object.cpp parser.cpp config.cpp parsers/json.cpp unicode.cpp app_parameters.cpp)
# Setup gtest
add_subdirectory(googletest/googletest)

277
src/test/app_parameters.cpp

@ -0,0 +1,277 @@
/**
* Copyright (C) 2020 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
*/
#include "test.hpp"
#include "app_parameters.hpp"
using namespace jessilib;
using namespace std::literals;
class ArgWrapper {
public:
template<typename... Args>
ArgWrapper(Args... in_args)
: ArgWrapper{ std::vector<std::string>{ in_args... } } {
// Empty ctor body
}
ArgWrapper(std::vector<std::string> in_args)
: m_args{ in_args },
m_argv{ new const char*[in_args.size()] } {
// Populate m_argv
for (size_t index = 0; index != m_args.size(); ++index) {
m_argv[index] = m_args[index].c_str();
}
}
const char** argv() const {
return m_argv.get();
}
int argc() const {
return m_args.size();
}
private:
std::vector<std::string> m_args;
std::unique_ptr<const char*[]> m_argv;
};
TEST(AppParametersTest, null) {
app_parameters parameters{ 0, static_cast<const char**>(nullptr) };
app_parameters parameters2{ 1234, static_cast<const char**>(nullptr) };
EXPECT_TRUE(parameters.path().empty());
EXPECT_TRUE(parameters.arguments().empty());
EXPECT_TRUE(parameters.switches().empty());
EXPECT_TRUE(parameters.switches_set().empty());
EXPECT_TRUE(parameters.values().empty());
EXPECT_TRUE(parameters.as_object().null());
EXPECT_TRUE(parameters2.path().empty());
EXPECT_TRUE(parameters2.arguments().empty());
EXPECT_TRUE(parameters2.switches().empty());
EXPECT_TRUE(parameters2.switches_set().empty());
EXPECT_TRUE(parameters2.values().empty());
EXPECT_TRUE(parameters2.as_object().null());
}
TEST(AppParametersTest, path_only) {
ArgWrapper args{ "/path/to/exe" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_EQ(parameters.path(), "/path/to/exe");
EXPECT_TRUE(parameters.arguments().empty());
EXPECT_TRUE(parameters.switches().empty());
EXPECT_TRUE(parameters.switches_set().empty());
EXPECT_TRUE(parameters.values().empty());
auto obj = parameters.as_object();
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
}
TEST(AppParametersTest, single_switch) {
ArgWrapper args{ "/path/to/exe", "-switch" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 1U);
EXPECT_EQ(parameters.switches().size(), 1U);
EXPECT_EQ(parameters.switches_set().size(), 1U);
EXPECT_TRUE(parameters.values().empty());
auto obj = parameters.as_object();
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], object{ std::vector<std::string>{ "-switch" } });
EXPECT_EQ(obj["Switches"], object{ std::vector<std::string>{ "switch" } });
}
TEST(AppParametersTest, double_switch) {
ArgWrapper args{ "/path/to/exe", "-switch1", "--switch2" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 2U);
EXPECT_EQ(parameters.switches_set().size(), 2U);
EXPECT_TRUE(parameters.values().empty());
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-switch1", "--switch2" };
std::vector<std::string> expected_switches{ "switch1", "switch2" };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Switches"], expected_switches);
}
TEST(AppParametersTest, duplicate_switch) {
ArgWrapper args{ "/path/to/exe", "-switch", "--switch" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 2U);
EXPECT_EQ(parameters.switches_set().size(), 1U);
EXPECT_TRUE(parameters.values().empty());
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-switch", "--switch" };
std::vector<std::string> expected_switches{ "switch", "switch" };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Switches"], expected_switches);
}
TEST(AppParametersTest, single_value) {
ArgWrapper args{ "/path/to/exe", "-key", "value" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().size(), 0U);
EXPECT_EQ(parameters.values().size(), 1U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-key", "value" };
std::map<std::string, object> expected_values{ { "key", "value" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Values"], expected_values);
}
TEST(AppParametersTest, single_value_eq) {
ArgWrapper args{ "/path/to/exe", "-key=value" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 1U);
EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().size(), 0U);
EXPECT_EQ(parameters.values().size(), 1U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-key=value" };
std::map<std::string, object> expected_values{ { "key", "value" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Values"], expected_values);
}
TEST(AppParametersTest, multiword_value) {
ArgWrapper args{ "/path/to/exe", "-key", "valuePart1", "valuePart2" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 3U);
EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().size(), 0U);
EXPECT_EQ(parameters.values().size(), 1U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-key", "valuePart1", "valuePart2" };
std::map<std::string, object> expected_values{ { "key", "valuePart1 valuePart2" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Values"], expected_values);
}
TEST(AppParametersTest, multiword_value_eq) {
ArgWrapper args{ "/path/to/exe", "-key=valuePart1", "valuePart2" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().size(), 0U);
EXPECT_EQ(parameters.values().size(), 1U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-key=valuePart1", "valuePart2" };
std::map<std::string, object> expected_values{ { "key", "valuePart1 valuePart2" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Values"], expected_values);
}
TEST(AppParametersTest, double_value) {
ArgWrapper args{ "/path/to/exe", "-key", "value", "--key2", "value2" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 4U);
EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().size(), 0U);
EXPECT_EQ(parameters.values().size(), 2U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-key", "value", "--key2", "value2" };
std::map<std::string, object> expected_values{ { "key", "value" }, { "key2", "value2" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Values"], expected_values);
}
TEST(AppParametersTest, double_value_eq) {
ArgWrapper args{ "/path/to/exe", "-key=value", "--key2=value2" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().size(), 0U);
EXPECT_EQ(parameters.values().size(), 2U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "-key=value", "--key2=value2" };
std::map<std::string, object> expected_values{ { "key", "value" }, { "key2", "value2" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Values"], expected_values);
}
TEST(AppParametersTest, switch_and_value) {
ArgWrapper args{ "/path/to/exe", "--switch", "-key", "value" };
app_parameters parameters{ args.argc(), args.argv() };
EXPECT_FALSE(parameters.path().empty());
EXPECT_EQ(parameters.arguments().size(), 3U);
EXPECT_EQ(parameters.switches().size(), 1U);
EXPECT_EQ(parameters.switches_set().size(), 1U);
EXPECT_EQ(parameters.values().size(), 1U);
auto obj = parameters.as_object();
std::vector<std::string> expected_args{ "--switch", "-key", "value" };
std::vector<std::string> expected_switches{ "switch" };
std::map<std::string, object> expected_values{ { "key", "value" } };
EXPECT_FALSE(obj.null());
EXPECT_EQ(obj["Path"], "/path/to/exe");
EXPECT_EQ(obj["Args"], expected_args);
EXPECT_EQ(obj["Switches"], expected_switches);
EXPECT_EQ(obj["Values"], expected_values);
}
Loading…
Cancel
Save