From 0b0bce78f60c54bd9d91dedc37258a6d75cf5053 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Mon, 7 Jun 2021 20:01:12 +0200 Subject: [PATCH] LibCore/ArgsParser: Add test suite This adds a very basic test suite for ArgsParser that we can use to set a baseline of functionality that we want to make sure keeps working. --- Tests/LibCore/CMakeLists.txt | 1 + Tests/LibCore/TestLibCoreArgsParser.cpp | 209 ++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 Tests/LibCore/TestLibCoreArgsParser.cpp diff --git a/Tests/LibCore/CMakeLists.txt b/Tests/LibCore/CMakeLists.txt index 1a949bfd2f..934d2982a2 100644 --- a/Tests/LibCore/CMakeLists.txt +++ b/Tests/LibCore/CMakeLists.txt @@ -1,5 +1,6 @@ set( TEST_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/TestLibCoreArgsParser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TestLibCoreFileWatcher.cpp ) diff --git a/Tests/LibCore/TestLibCoreArgsParser.cpp b/Tests/LibCore/TestLibCoreArgsParser.cpp new file mode 100644 index 0000000000..8292e20369 --- /dev/null +++ b/Tests/LibCore/TestLibCoreArgsParser.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +static char** build_argv(Vector arguments) +{ + auto argv = new char*[arguments.size() + 1]; + size_t idx = 0; + for (auto& argument : arguments) { + auto char_argument = new char[argument.length() + 1]; + memcpy(char_argument, argument.characters(), argument.length()); + char_argument[argument.length()] = '\0'; + argv[idx++] = char_argument; + } + argv[idx++] = nullptr; + return argv; +} + +static void delete_argv(char** argv, size_t number_of_arguments) +{ + for (size_t idx = 0; idx < number_of_arguments; ++idx) + delete[] argv[idx]; + delete[] argv; +} + +static bool run_parser(Vector arguments, Function parser_initialization = {}) +{ + Core::ArgsParser parser; + if (parser_initialization) + parser_initialization(parser); + + auto argv = build_argv(arguments); + auto result = parser.parse(arguments.size(), argv, Core::ArgsParser::FailureBehavior::Ignore); + delete_argv(argv, arguments.size()); + + return result; +} + +TEST_CASE(no_arguments) +{ + auto parser_result = run_parser({ "app" }); + EXPECT_EQ(parser_result, true); +} + +TEST_CASE(bool_option) +{ + // Short option + bool force = false; + auto parser_result = run_parser({ "app", "-f" }, [&](auto& parser) { + parser.add_option(force, "force", nullptr, 'f'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, true); + + // Short option, not given + force = false; + parser_result = run_parser({ "app" }, [&](auto& parser) { + parser.add_option(force, "force", nullptr, 'f'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, false); + + // Long option + force = false; + parser_result = run_parser({ "app", "--force" }, [&](auto& parser) { + parser.add_option(force, "force", "force", '\0'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, true); + + // Long option, not given + force = false; + parser_result = run_parser({ "app" }, [&](auto& parser) { + parser.add_option(force, "force", "force", '\0'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, false); + + // Allow both short and long option, provide short + force = false; + parser_result = run_parser({ "app", "-f" }, [&](auto& parser) { + parser.add_option(force, "force", "force", 'f'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, true); + + // Allow both short and long option, provide long + force = false; + parser_result = run_parser({ "app", "--force" }, [&](auto& parser) { + parser.add_option(force, "force", "force", 'f'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, true); + + // Allow both short and long option, provide both + force = false; + parser_result = run_parser({ "app", "--force", "-f" }, [&](auto& parser) { + parser.add_option(force, "force", "force", 'f'); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(force, true); +} + +TEST_CASE(positional_string_argument) +{ + // Single required string argument + String name = ""; + auto parser_result = run_parser({ "app", "buggie" }, [&](auto& parser) { + parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::Yes); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(name, "buggie"); + + // Single required string argument, not given + name = ""; + parser_result = run_parser({ "app" }, [&](auto& parser) { + parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::Yes); + }); + EXPECT_EQ(parser_result, false); + EXPECT_EQ(name, ""); + + // Single optional string argument + name = ""; + parser_result = run_parser({ "app", "buggie" }, [&](auto& parser) { + parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::No); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(name, "buggie"); + + // Single optional string argument, not given + name = ""; + parser_result = run_parser({ "app" }, [&](auto& parser) { + parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::No); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(name, ""); +} + +TEST_CASE(positional_vector_string_argument) +{ + // Zero or more positional arguments, zero given + Vector values = {}; + auto parser_result = run_parser({ "app" }, [&](auto& parser) { + parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(values.size(), 0u); + + // Zero or more positional arguments, one given + values = {}; + parser_result = run_parser({ "app", "one" }, [&](auto& parser) { + parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(values.size(), 1u); + if (values.size() == 1u) + EXPECT_EQ(values[0], "one"); + + // Zero or more positional arguments, two given + values = {}; + parser_result = run_parser({ "app", "one", "two" }, [&](auto& parser) { + parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(values.size(), 2u); + if (values.size() == 2u) { + EXPECT_EQ(values[0], "one"); + EXPECT_EQ(values[1], "two"); + } + + // One or more positional arguments, zero given + values = {}; + parser_result = run_parser({ "app" }, [&](auto& parser) { + parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes); + }); + EXPECT_EQ(parser_result, false); + EXPECT_EQ(values.size(), 0u); + + // One or more positional arguments, one given + values = {}; + parser_result = run_parser({ "app", "one" }, [&](auto& parser) { + parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(values.size(), 1u); + if (values.size() == 1u) + EXPECT_EQ(values[0], "one"); + + // One or more positional arguments, two given + values = {}; + parser_result = run_parser({ "app", "one", "two" }, [&](auto& parser) { + parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes); + }); + EXPECT_EQ(parser_result, true); + EXPECT_EQ(values.size(), 2u); + if (values.size() == 2u) { + EXPECT_EQ(values[0], "one"); + EXPECT_EQ(values[1], "two"); + } +} +}