// Copyright 2022, FOSS-VG Developers and Contributers
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

#include <string>
#include <iostream>
#include <vector>
#include "assert.hpp"
#include "../lib/error.hpp"

#include "../lib/cli.hpp"

int main() {
    std::cout << "################################################################################" << std::endl;
    std::cout << "CLI argument parsing tests" << std::endl;
    std::cout << "################################################################################" << std::endl;

    // Valid parameter test ############################################
    // This test exercises the basic functionality of the arguments parser
    // with many variations of valid input on a single command line.
    //
    // simulated command line:
    // test -0 -ab -12345 --long-flag -cconcatenated -d separate-value -efdouble-concatenated "argument 0" -gh concatenated-separate-value --long-argument-with-value-included="included value" --long-argument-with-value-separated "separate value" "argument 1" "argument 2"

    std::vector<CLI::Flag> validTestFlags;
    validTestFlags.push_back(CLI::Flag('0', "00000", "a short flag on its own"));
    validTestFlags.push_back(CLI::Flag('a', "aaaaa", "concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('b', "bbbbb", "concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('1', "11111", "more than two concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('2', "22222", "more than two concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('3', "33333", "more than two concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('4', "44444", "more than two concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('5', "55555", "more than two concatenated short flags"));
    validTestFlags.push_back(CLI::Flag('l', "long-flag", "long flags"));
    validTestFlags.push_back(CLI::Flag('e', "eeeee", "short flags concatenated with an argument that is concatenated with its value"));
    validTestFlags.push_back(CLI::Flag('g', "ggggg", "short flags concatenated with an argument that has a separate value"));
    validTestFlags.push_back(CLI::Flag('6', "66666", "unused flag"));

    std::vector<CLI::Option> validTestOptions;
    validTestOptions.push_back(CLI::Option('c', "ccccc", "VALUE", "short argument concatenated with its value"));
    validTestOptions.push_back(CLI::Option('d', "ddddd", " VALUE", "short argument with separate value"));
    validTestOptions.push_back(CLI::Option('f', "fffff", "VALUE", "short argument concatenated with a flag and its value"));
    validTestOptions.push_back(CLI::Option('h', "hhhhh", " VALUE", "short argument concatenated with a flag with separate value"));
    validTestOptions.push_back(CLI::Option('x', "long-argument-with-value-included", "VALUE", "long argument with its value included using ="));
    validTestOptions.push_back(CLI::Option('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value"));
    validTestOptions.push_back(CLI::Option('z', "zzzzz", "NOPE", "unused argument"));

    std::vector<CLI::Argument> validTestArguments;
    validTestArguments.push_back(CLI::Argument("argument0", "argument between optional parameters"));
    validTestArguments.push_back(CLI::Argument("argument1", "argument"));
    validTestArguments.push_back(CLI::Argument("argument2", "argument"));

    const char** validTestParameterList = new const char*[17];
    validTestParameterList[ 0] = "test";
    validTestParameterList[ 1] = "-0";
    validTestParameterList[ 2] = "-ab";
    validTestParameterList[ 3] = "-12345";
    validTestParameterList[ 4] = "--long-flag";
    validTestParameterList[ 5] = "-cconcatenated";
    validTestParameterList[ 6] = "-d";
    validTestParameterList[ 7] = "separate-value";
    validTestParameterList[ 8] = "-efdouble-concatenated";
    validTestParameterList[ 9] = "argument 0";
    validTestParameterList[10] = "-gh";
    validTestParameterList[11] = "concatenated-separate-value";
    validTestParameterList[12] = "--long-argument-with-value-included=included value";
    validTestParameterList[13] = "--long-argument-with-value-separated";
    validTestParameterList[14] = "separate value";
    validTestParameterList[15] = "argument 1";
    validTestParameterList[16] = "argument 2";
    int validTestParameterCount = 17;

    CLI::ArgumentsParser validTestParameterParser = CLI::ArgumentsParser(validTestParameterCount, validTestParameterList, validTestFlags, validTestOptions, validTestArguments);

    ASSERT(!validTestParameterParser.wrongUsage);
    ASSERT(validTestParameterParser.programName == std::string("test"));
    ASSERT(!validTestParameterParser.getFlag('0').isError);
    ASSERT(validTestParameterParser.getFlag('0').value);
    ASSERT(validTestParameterParser.getFlag(std::string("00000")).value);
    ASSERT(!validTestParameterParser.getFlag('a').isError);
    ASSERT(validTestParameterParser.getFlag('a').value);
    ASSERT(validTestParameterParser.getFlag(std::string("aaaaa")).value);
    ASSERT(!validTestParameterParser.getFlag('b').isError);
    ASSERT(validTestParameterParser.getFlag('b').value);
    ASSERT(validTestParameterParser.getFlag(std::string("bbbbb")).value);
    ASSERT(!validTestParameterParser.getFlag('1').isError);
    ASSERT(validTestParameterParser.getFlag('1').value);
    ASSERT(validTestParameterParser.getFlag(std::string("11111")).value);
    ASSERT(!validTestParameterParser.getFlag('2').isError);
    ASSERT(validTestParameterParser.getFlag('2').value);
    ASSERT(validTestParameterParser.getFlag(std::string("22222")).value);
    ASSERT(!validTestParameterParser.getFlag('3').isError);
    ASSERT(validTestParameterParser.getFlag('3').value);
    ASSERT(validTestParameterParser.getFlag(std::string("33333")).value);
    ASSERT(!validTestParameterParser.getFlag('4').isError);
    ASSERT(validTestParameterParser.getFlag('4').value);
    ASSERT(validTestParameterParser.getFlag(std::string("44444")).value);
    ASSERT(!validTestParameterParser.getFlag('5').isError);
    ASSERT(validTestParameterParser.getFlag('5').value);
    ASSERT(validTestParameterParser.getFlag(std::string("55555")).value);
    ASSERT(!validTestParameterParser.getFlag(std::string("long-flag")).isError);
    ASSERT(validTestParameterParser.getFlag('l').value);
    ASSERT(validTestParameterParser.getFlag(std::string("long-flag")).value);
    ASSERT(!validTestParameterParser.getFlag('6').isError);
    ASSERT(!validTestParameterParser.getFlag('6').value);
    ASSERT(!validTestParameterParser.getFlag(std::string("66666")).value);
    ASSERT(!validTestParameterParser.getOption('c').isError);
    ASSERT(validTestParameterParser.getOption('c').value==std::string("concatenated"));
    ASSERT(validTestParameterParser.getOption(std::string("ccccc")).value==std::string("concatenated"));
    ASSERT(!validTestParameterParser.getOption('d').isError);
    ASSERT(validTestParameterParser.getOption('d').value==std::string("separate-value"));
    ASSERT(validTestParameterParser.getOption(std::string("ddddd")).value==std::string("separate-value"));
    ASSERT(!validTestParameterParser.getFlag('e').isError);
    ASSERT(validTestParameterParser.getFlag('e').value);
    ASSERT(validTestParameterParser.getFlag(std::string("eeeee")).value);
    ASSERT(!validTestParameterParser.getOption('f').isError);
    ASSERT(validTestParameterParser.getOption('f').value==std::string("double-concatenated"));
    ASSERT(validTestParameterParser.getOption(std::string("fffff")).value==std::string("double-concatenated"));
    ASSERT(!validTestParameterParser.getArgument(0).isError);
    ASSERT(validTestParameterParser.getArgument(0).value=="argument 0");
    ASSERT(!validTestParameterParser.getFlag('g').isError);
    ASSERT(validTestParameterParser.getFlag('g').value);
    ASSERT(validTestParameterParser.getFlag(std::string("ggggg")).value);
    ASSERT(!validTestParameterParser.getOption('h').isError);
    ASSERT(validTestParameterParser.getOption('h').value==std::string("concatenated-separate-value"));
    ASSERT(validTestParameterParser.getOption(std::string("hhhhh")).value==std::string("concatenated-separate-value"));
    ASSERT(!validTestParameterParser.getOption('x').isError);
    ASSERT(validTestParameterParser.getOption('x').value==std::string("included value"));
    ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-included")).value==std::string("included value"));
    ASSERT(!validTestParameterParser.getOption('y').isError);
    ASSERT(validTestParameterParser.getOption('y').value==std::string("separate value"));
    ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-separated")).value==std::string("separate value"));
    ASSERT(!validTestParameterParser.getArgument(1).isError);
    ASSERT(validTestParameterParser.getArgument(1).value=="argument 1");
    ASSERT(!validTestParameterParser.getArgument(2).isError);
    ASSERT(validTestParameterParser.getArgument(2).value=="argument 2");
    ASSERT(!validTestParameterParser.getOption('z').isError);
    ASSERT(validTestParameterParser.getOption('z').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(validTestParameterParser.getOption('z').value == std::string(""));
    ASSERT(validTestParameterParser.wrongUsageMessages.size() == 0);

    delete[] validTestParameterList;
    std::cout << "Passed valid command line test." << std::endl;

    // empty input (valid and invalid) #################################
    std::vector<CLI::Flag> emptyTestFlags;

    std::vector<CLI::Option> emptyTestOptions;

    std::vector<CLI::Argument> emptyTestArguments;

    const char** emptyTestParameterList = new const char*[1];
    // The command is always a part of a command line, even if it is empty
    // otherwise. Therefore, I think it is reasonable to assume that it
    // will always be available.
    emptyTestParameterList[0] = "test";
    int emptyTestParameterCount = 1;

    // valid
    CLI::ArgumentsParser validEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestOptions, emptyTestArguments);
    ASSERT(!validEmptyTestParameterParser.wrongUsage);
    ASSERT(validEmptyTestParameterParser.programName == std::string("test"));
    ASSERT(validEmptyTestParameterParser.wrongUsageMessages.size() == 0);

    //invalid
    emptyTestArguments.push_back(CLI::Argument("argument", "argument"));

    CLI::ArgumentsParser invalidEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestOptions, emptyTestArguments);
    ASSERT(invalidEmptyTestParameterParser.wrongUsage);
    ASSERT(invalidEmptyTestParameterParser.programName == std::string("test"));
    ASSERT(invalidEmptyTestParameterParser.getArgument(0).isError);
    ASSERT(invalidEmptyTestParameterParser.getArgument(0).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(invalidEmptyTestParameterParser.wrongUsageMessages.size() == 1);
    ASSERT(invalidEmptyTestParameterParser.wrongUsageMessages.at(0) == "Too few arguments! Missing: argument");

    delete[] emptyTestParameterList;
    std::cout << "Passed empty command line test." << std::endl;

    // unknown flag ####################################################

    std::vector<CLI::Flag> unknownFlagTestFlags;
    unknownFlagTestFlags.push_back(CLI::Flag('0', "zero", "test flag"));
    unknownFlagTestFlags.push_back(CLI::Flag('1', "one",  "test flag"));
    unknownFlagTestFlags.push_back(CLI::Flag('2', "two",  "test flag"));
    unknownFlagTestFlags.push_back(CLI::Flag('3', "three","test flag"));
    unknownFlagTestFlags.push_back(CLI::Flag('4', "four", "test flag"));
    unknownFlagTestFlags.push_back(CLI::Flag('5', "five", "test flag"));

    std::vector<CLI::Option> unknownFlagTestOptions;

    std::vector<CLI::Argument> unknownFlagTestArguments;

    const char** unknownFlagTestParameterList = new const char*[7];
    unknownFlagTestParameterList[0] = "test";
    unknownFlagTestParameterList[1] = "-a";
    unknownFlagTestParameterList[2] = "-1";
    unknownFlagTestParameterList[3] = "-2";
    unknownFlagTestParameterList[4] = "-3";
    unknownFlagTestParameterList[5] = "-4";
    unknownFlagTestParameterList[6] = "-5";
    int unknownFlagStandaloneTestParameterCount = 2;

    CLI::ArgumentsParser unknownFlagStandaloneTestParser = CLI::ArgumentsParser(unknownFlagStandaloneTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestOptions, unknownFlagTestArguments);
    ASSERT(unknownFlagStandaloneTestParser.getFlag('1').isError);
    ASSERT(unknownFlagStandaloneTestParser.getFlag('1').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(!unknownFlagStandaloneTestParser.getFlag('1').value);
    ASSERT(unknownFlagStandaloneTestParser.getFlag(std::string("four")).isError);
    ASSERT(unknownFlagStandaloneTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(!unknownFlagStandaloneTestParser.getFlag(std::string("four")).value);
    ASSERT(unknownFlagStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownFlagStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a");

    int unknownFlagMixedTestParameterCount = 7;

    CLI::ArgumentsParser unknownFlagMixedTestParser = CLI::ArgumentsParser(unknownFlagMixedTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestOptions, unknownFlagTestArguments);
    ASSERT(unknownFlagMixedTestParser.getFlag('1').isError);
    ASSERT(unknownFlagMixedTestParser.getFlag('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownFlagMixedTestParser.getFlag('1').value);
    ASSERT(unknownFlagMixedTestParser.getFlag(std::string("four")).isError);
    ASSERT(unknownFlagMixedTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownFlagMixedTestParser.getFlag(std::string("four")).value);
    ASSERT(unknownFlagMixedTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownFlagMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a");

    delete[] unknownFlagTestParameterList;

    std::vector<CLI::Flag> unknownLongFlagTestFlags;
    unknownLongFlagTestFlags.push_back(CLI::Flag('0', "zero", "test flag"));
    unknownLongFlagTestFlags.push_back(CLI::Flag('1', "one",  "test flag"));
    unknownLongFlagTestFlags.push_back(CLI::Flag('2', "two",  "test flag"));
    unknownLongFlagTestFlags.push_back(CLI::Flag('3', "three","test flag"));
    unknownLongFlagTestFlags.push_back(CLI::Flag('4', "four", "test flag"));
    unknownLongFlagTestFlags.push_back(CLI::Flag('5', "five", "test flag"));

    std::vector<CLI::Option> unknownLongFlagTestOptions;

    std::vector<CLI::Argument> unknownLongFlagTestArguments;

    const char** unknownLongFlagTestParameterList = new const char*[7];
    unknownLongFlagTestParameterList[0] = "test";
    unknownLongFlagTestParameterList[1] = "--a";
    unknownLongFlagTestParameterList[2] = "-1";
    unknownLongFlagTestParameterList[3] = "-2";
    unknownLongFlagTestParameterList[4] = "-3";
    unknownLongFlagTestParameterList[5] = "-4";
    unknownLongFlagTestParameterList[6] = "-5";
    int unknownLongFlagStandaloneTestParameterCount = 2;

    CLI::ArgumentsParser unknownLongFlagStandaloneTestParser = CLI::ArgumentsParser(unknownLongFlagStandaloneTestParameterCount, unknownLongFlagTestParameterList, unknownLongFlagTestFlags, unknownLongFlagTestOptions, unknownLongFlagTestArguments);
    ASSERT(unknownLongFlagStandaloneTestParser.getFlag('1').isError);
    ASSERT(unknownLongFlagStandaloneTestParser.getFlag('1').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(!unknownLongFlagStandaloneTestParser.getFlag('1').value);
    ASSERT(unknownLongFlagStandaloneTestParser.getFlag(std::string("four")).isError);
    ASSERT(unknownLongFlagStandaloneTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(!unknownLongFlagStandaloneTestParser.getFlag(std::string("four")).value);
    ASSERT(unknownLongFlagStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownLongFlagStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag: --a");

    int unknownLongFlagMixedTestParameterCount = 7;

    CLI::ArgumentsParser unknownLongFlagMixedTestParser = CLI::ArgumentsParser(unknownLongFlagMixedTestParameterCount, unknownLongFlagTestParameterList, unknownLongFlagTestFlags, unknownLongFlagTestOptions, unknownLongFlagTestArguments);
    ASSERT(unknownLongFlagMixedTestParser.getFlag('1').isError);
    ASSERT(unknownLongFlagMixedTestParser.getFlag('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownLongFlagMixedTestParser.getFlag('1').value);
    ASSERT(unknownLongFlagMixedTestParser.getFlag(std::string("four")).isError);
    ASSERT(unknownLongFlagMixedTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownLongFlagMixedTestParser.getFlag(std::string("four")).value);
    ASSERT(unknownLongFlagMixedTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownLongFlagMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag: --a");

    delete[] unknownLongFlagTestParameterList;
    std::cout << "Passed unknown flag test." << std::endl;

    // unknown argument ################################################

    std::vector<CLI::Flag> unknownOptionTestFlags;

    std::vector<CLI::Option> unknownOptionTestOptions;
    unknownOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test argument"));
    unknownOptionTestOptions.push_back(CLI::Option('1', "one",  "one",  "test argument"));
    unknownOptionTestOptions.push_back(CLI::Option('2', "two",  "two",  "test argument"));
    unknownOptionTestOptions.push_back(CLI::Option('3', "three","three","test argument"));
    unknownOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test argument"));
    unknownOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test argument"));

    std::vector<CLI::Argument> unknownOptionTestArguments;

    const char** unknownOptionTestParameterList = new const char*[7];
    unknownOptionTestParameterList[0] = "test";
    unknownOptionTestParameterList[1] = "-a123";
    unknownOptionTestParameterList[2] = "-1a";
    unknownOptionTestParameterList[3] = "-2b";
    unknownOptionTestParameterList[4] = "-3c";
    unknownOptionTestParameterList[5] = "-4d";
    unknownOptionTestParameterList[6] = "-5e";
    int unknownOptionStandaloneTestParameterCount = 2;

    CLI::ArgumentsParser unknownOptionStandaloneTestParser = CLI::ArgumentsParser(unknownOptionStandaloneTestParameterCount, unknownOptionTestParameterList, unknownOptionTestFlags, unknownOptionTestOptions, unknownOptionTestArguments);
    ASSERT(unknownOptionStandaloneTestParser.getOption('1').isError);
    ASSERT(unknownOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(unknownOptionStandaloneTestParser.getOption('1').value == "");
    ASSERT(unknownOptionStandaloneTestParser.getOption(std::string("four")).isError);
    ASSERT(unknownOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(unknownOptionStandaloneTestParser.getOption(std::string("four")).value == "");
    ASSERT(unknownOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a123");

    int unknownOptionMixedTestParameterCount = 7;

    CLI::ArgumentsParser unknownOptionMixedTestParser = CLI::ArgumentsParser(unknownOptionMixedTestParameterCount, unknownOptionTestParameterList, unknownOptionTestFlags, unknownOptionTestOptions, unknownOptionTestArguments);
    ASSERT(unknownOptionMixedTestParser.getOption('1').isError);
    ASSERT(unknownOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownOptionMixedTestParser.getOption('1').value == "a");
    ASSERT(unknownOptionMixedTestParser.getOption(std::string("four")).isError);
    ASSERT(unknownOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownOptionMixedTestParser.getOption(std::string("four")).value == "d");
    ASSERT(unknownOptionMixedTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownOptionMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a123");

    delete[] unknownOptionTestParameterList;

    std::vector<CLI::Flag> unknownLongOptionTestFlags;

    std::vector<CLI::Option> unknownLongOptionTestOptions;
    unknownLongOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test argument"));
    unknownLongOptionTestOptions.push_back(CLI::Option('1', "one",  "one",  "test argument"));
    unknownLongOptionTestOptions.push_back(CLI::Option('2', "two",  "two",  "test argument"));
    unknownLongOptionTestOptions.push_back(CLI::Option('3', "three","three","test argument"));
    unknownLongOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test argument"));
    unknownLongOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test argument"));

    std::vector<CLI::Argument> unknownLongOptionTestArguments;

    const char** unknownLongOptionTestParameterList = new const char*[7];
    unknownLongOptionTestParameterList[0] = "test";
    unknownLongOptionTestParameterList[1] = "--a=123";
    unknownLongOptionTestParameterList[2] = "-1a";
    unknownLongOptionTestParameterList[3] = "-2b";
    unknownLongOptionTestParameterList[4] = "-3c";
    unknownLongOptionTestParameterList[5] = "-4d";
    unknownLongOptionTestParameterList[6] = "-5e";
    int unknownLongOptionStandaloneTestParameterCount = 2;

    CLI::ArgumentsParser unknownLongOptionStandaloneTestParser = CLI::ArgumentsParser(unknownLongOptionStandaloneTestParameterCount, unknownLongOptionTestParameterList, unknownLongOptionTestFlags, unknownLongOptionTestOptions, unknownLongOptionTestArguments);
    ASSERT(unknownLongOptionStandaloneTestParser.getOption('1').isError);
    ASSERT(unknownLongOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(unknownLongOptionStandaloneTestParser.getOption('1').value == "");
    ASSERT(unknownLongOptionStandaloneTestParser.getOption(std::string("four")).isError);
    ASSERT(unknownLongOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(unknownLongOptionStandaloneTestParser.getOption(std::string("four")).value == "");
    ASSERT(unknownLongOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownLongOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument (or it's a flag that doesn't take a value): --a=123");

    int unknownLongOptionMixedTestParameterCount = 7;

    CLI::ArgumentsParser unknownLongOptionMixedTestParser = CLI::ArgumentsParser(unknownLongOptionMixedTestParameterCount, unknownLongOptionTestParameterList, unknownLongOptionTestFlags, unknownLongOptionTestOptions, unknownLongOptionTestArguments);
    ASSERT(unknownLongOptionMixedTestParser.getOption('1').isError);
    ASSERT(unknownLongOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownLongOptionMixedTestParser.getOption('1').value == "a");
    ASSERT(unknownLongOptionMixedTestParser.getOption(std::string("four")).isError);
    ASSERT(unknownLongOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(unknownLongOptionMixedTestParser.getOption(std::string("four")).value == "d");
    ASSERT(unknownLongOptionMixedTestParser.wrongUsageMessages.size() == 1);
    ASSERT(unknownLongOptionMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument (or it's a flag that doesn't take a value): --a=123");

    delete[] unknownLongOptionTestParameterList;
    std::cout << "Passed unknown option test." << std::endl;

    // incomplete option ###############################################

    std::vector<CLI::Flag> incompleteOptionTestFlags;

    std::vector<CLI::Option> incompleteOptionTestOptions;
    incompleteOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test option"));
    incompleteOptionTestOptions.push_back(CLI::Option('1', "one",  "one",  "test option"));
    incompleteOptionTestOptions.push_back(CLI::Option('2', "two",  "two",  "test option"));
    incompleteOptionTestOptions.push_back(CLI::Option('3', "three","three","test option"));
    incompleteOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test option"));
    incompleteOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test option"));

    std::vector<CLI::Argument> incompleteOptionTestArguments;

    const char** incompleteOptionTestParameterList = new const char*[6];
    incompleteOptionTestParameterList[0] = "test";
    incompleteOptionTestParameterList[1] = "-1";
    incompleteOptionTestParameterList[2] = "--two="; // value ""
    incompleteOptionTestParameterList[3] = "-3c";
    incompleteOptionTestParameterList[4] = "-4";
    incompleteOptionTestParameterList[5] = "-5e";
    int incompleteOptionStandaloneTestParameterCount = 2;

    CLI::ArgumentsParser incompleteOptionStandaloneTestParser = CLI::ArgumentsParser(incompleteOptionStandaloneTestParameterCount, incompleteOptionTestParameterList, incompleteOptionTestFlags, incompleteOptionTestOptions, incompleteOptionTestArguments);
    ASSERT(incompleteOptionStandaloneTestParser.getOption('1').isError);
    ASSERT(incompleteOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteOptionStandaloneTestParser.getOption('1').value == "");
    ASSERT(incompleteOptionStandaloneTestParser.getOption(std::string("four")).isError);
    ASSERT(incompleteOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(incompleteOptionStandaloneTestParser.getOption(std::string("four")).value == "");
    ASSERT(incompleteOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(incompleteOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");

    int incompleteOptionMixedTestParameterCount = 6;

    CLI::ArgumentsParser incompleteOptionMixedTestParser = CLI::ArgumentsParser(incompleteOptionMixedTestParameterCount, incompleteOptionTestParameterList, incompleteOptionTestFlags, incompleteOptionTestOptions, incompleteOptionTestArguments);
    ASSERT(incompleteOptionMixedTestParser.getOption('1').isError);
    ASSERT(incompleteOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteOptionMixedTestParser.getOption('1').value == "");
    ASSERT(incompleteOptionMixedTestParser.getOption(std::string("four")).isError);
    ASSERT(incompleteOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteOptionMixedTestParser.getOption(std::string("four")).value == "");
    ASSERT(incompleteOptionMixedTestParser.getOption('5').isError);
    ASSERT(incompleteOptionMixedTestParser.getOption('5').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteOptionMixedTestParser.getOption('5').value == "e");
    ASSERT(incompleteOptionMixedTestParser.wrongUsageMessages.size() == 2);
    ASSERT(incompleteOptionMixedTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");
    ASSERT(incompleteOptionMixedTestParser.wrongUsageMessages.at(1) == "Argument expects value but has none: four");

    delete[] incompleteOptionTestParameterList;

    std::vector<CLI::Flag> incompleteLongOptionTestFlags;

    std::vector<CLI::Option> incompleteLongOptionTestOptions;
    incompleteLongOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test argument"));
    incompleteLongOptionTestOptions.push_back(CLI::Option('1', "one",  "one",  "test argument"));
    incompleteLongOptionTestOptions.push_back(CLI::Option('2', "two",  "two",  "test argument"));
    incompleteLongOptionTestOptions.push_back(CLI::Option('3', "three","three","test argument"));
    incompleteLongOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test argument"));
    incompleteLongOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test argument"));

    std::vector<CLI::Argument> incompleteLongOptionTestArguments;

    const char** incompleteLongOptionTestParameterList = new const char*[8];
    incompleteLongOptionTestParameterList[0] = "test";
    incompleteLongOptionTestParameterList[1] = "--one";
    incompleteLongOptionTestParameterList[2] = "--two";
    incompleteLongOptionTestParameterList[3] = "b";
    incompleteLongOptionTestParameterList[4] = "-3c";
    incompleteLongOptionTestParameterList[5] = "arg";
    incompleteLongOptionTestParameterList[6] = "--four";
    incompleteLongOptionTestParameterList[7] = "--five=e";
    int incompleteLongOptionStandaloneTestParameterCount = 2;

    CLI::ArgumentsParser incompleteLongOptionStandaloneTestParser = CLI::ArgumentsParser(incompleteLongOptionStandaloneTestParameterCount, incompleteLongOptionTestParameterList, incompleteLongOptionTestFlags, incompleteLongOptionTestOptions, incompleteLongOptionTestArguments);
    ASSERT(incompleteLongOptionStandaloneTestParser.getOption('1').isError);
    ASSERT(incompleteLongOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteLongOptionStandaloneTestParser.getOption('1').value == "");
    ASSERT(incompleteLongOptionStandaloneTestParser.getOption(std::string("four")).isError);
    ASSERT(incompleteLongOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(incompleteLongOptionStandaloneTestParser.getOption(std::string("four")).value == "");
    ASSERT(incompleteLongOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(incompleteLongOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");

    int incompleteLongOptionMixedTestParameterCount = 8;
    incompleteLongOptionTestArguments.push_back(CLI::Argument("arg", "argument"));

    CLI::ArgumentsParser incompleteLongOptionMixedTestParser = CLI::ArgumentsParser(incompleteLongOptionMixedTestParameterCount, incompleteLongOptionTestParameterList, incompleteLongOptionTestFlags, incompleteLongOptionTestOptions, incompleteLongOptionTestArguments);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('1').isError);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('1').value == "");
    ASSERT(incompleteLongOptionMixedTestParser.getOption('2').isError);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('2').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('2').value == "b");
    ASSERT(incompleteLongOptionMixedTestParser.getOption(std::string("four")).isError);
    ASSERT(incompleteLongOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteLongOptionMixedTestParser.getOption(std::string("four")).value == "");
    ASSERT(incompleteLongOptionMixedTestParser.getOption('5').isError);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('5').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(incompleteLongOptionMixedTestParser.getOption('5').value == "e");
    ASSERT(incompleteLongOptionMixedTestParser.wrongUsageMessages.size() == 2);
    ASSERT(incompleteLongOptionMixedTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");
    ASSERT(incompleteLongOptionMixedTestParser.wrongUsageMessages.at(1) == "Argument expects value but has none: four");

    delete[] incompleteLongOptionTestParameterList;
    std::cout << "Passed incomplete option test." << std::endl;

    // too few arguments ###############################################

    std::vector<CLI::Flag> tooFewArgumentsTestFlags;
    tooFewArgumentsTestFlags.push_back(CLI::Flag('a', "aaaaa", "test flag"));

    std::vector<CLI::Option> tooFewArgumentsTestOptions;
    tooFewArgumentsTestOptions.push_back(CLI::Option('b', "bbbbb", "b", "test argument"));
    tooFewArgumentsTestOptions.push_back(CLI::Option('c', "ccccc", "c", "test argument"));
    tooFewArgumentsTestOptions.push_back(CLI::Option('d', "ddddd", "d", "test argument"));

    std::vector<CLI::Argument> tooFewArgumentsTestArguments;
    tooFewArgumentsTestArguments.push_back(CLI::Argument("argument0", "test argument"));
    tooFewArgumentsTestArguments.push_back(CLI::Argument("argument1", "test argument"));
    tooFewArgumentsTestArguments.push_back(CLI::Argument("argument2", "test argument"));
    tooFewArgumentsTestArguments.push_back(CLI::Argument("argument3", "test argument"));
    tooFewArgumentsTestArguments.push_back(CLI::Argument("argument4", "test argument"));

    const char** tooFewArgumentsTestParameterList = new const char*[10];
    tooFewArgumentsTestParameterList[0] = "test";
    tooFewArgumentsTestParameterList[1] = "arg_0";
    tooFewArgumentsTestParameterList[2] = "-a";//
    tooFewArgumentsTestParameterList[3] = "-b";
    tooFewArgumentsTestParameterList[4] = "1";
    tooFewArgumentsTestParameterList[5] = "-c5";
    tooFewArgumentsTestParameterList[6] = "arg_1";
    tooFewArgumentsTestParameterList[7] = "--ddddd";
    tooFewArgumentsTestParameterList[8] = "ddd";
    tooFewArgumentsTestParameterList[9] = "arg_2";

    int tooFewArgumentsStandaloneTestParameterCount = 2;
    CLI::ArgumentsParser tooFewArgumentsStandaloneTestParser = CLI::ArgumentsParser(tooFewArgumentsStandaloneTestParameterCount, tooFewArgumentsTestParameterList, tooFewArgumentsTestFlags, tooFewArgumentsTestOptions, tooFewArgumentsTestArguments);

    ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(0).isError);
    ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(0).value == "arg_0");
    ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(1).isError);
    ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(1).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(1).value == "");
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('b').isError);
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('b').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('b').value == "");
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('c').isError);
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('c').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('c').value == "");
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('d').isError);
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('d').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooFewArgumentsStandaloneTestParser.getOption('d').value == "");
    ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.size() == 4);
    ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(0) == "Too few arguments! Missing: argument1");
    ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(1) == "Too few arguments! Missing: argument2");
    ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(2) == "Too few arguments! Missing: argument3");
    ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(3) == "Too few arguments! Missing: argument4");

    int tooFewArgumentsMixedTestParameterCount = 10;
    CLI::ArgumentsParser tooFewArgumentsMixedTestParser = CLI::ArgumentsParser(tooFewArgumentsMixedTestParameterCount, tooFewArgumentsTestParameterList, tooFewArgumentsTestFlags, tooFewArgumentsTestOptions, tooFewArgumentsTestArguments);

    ASSERT(tooFewArgumentsMixedTestParser.getArgument(0).isError);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(0).value == "arg_0");
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(1).isError);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(1).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(1).value == "arg_1");
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(2).isError);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(2).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(2).value == "arg_2");
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(3).isError);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(3).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(3).value == "");
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(4).isError);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(4).errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooFewArgumentsMixedTestParser.getArgument(4).value == "");
    ASSERT(tooFewArgumentsMixedTestParser.getOption('b').isError);
    ASSERT(tooFewArgumentsMixedTestParser.getOption('b').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsMixedTestParser.getOption('b').value == "1");
    ASSERT(tooFewArgumentsMixedTestParser.getOption('c').isError);
    ASSERT(tooFewArgumentsMixedTestParser.getOption('c').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsMixedTestParser.getOption('c').value == "5");
    ASSERT(tooFewArgumentsMixedTestParser.getOption('d').isError);
    ASSERT(tooFewArgumentsMixedTestParser.getOption('d').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooFewArgumentsMixedTestParser.getOption('d').value == "ddd");
    ASSERT(tooFewArgumentsMixedTestParser.wrongUsageMessages.size() == 2);
    ASSERT(tooFewArgumentsMixedTestParser.wrongUsageMessages.at(0) == "Too few arguments! Missing: argument3");
    ASSERT(tooFewArgumentsMixedTestParser.wrongUsageMessages.at(1) == "Too few arguments! Missing: argument4");

    delete[] tooFewArgumentsTestParameterList;
    std::cout << "Passed too few arguments test." << std::endl;

    // too many arguments ##############################################

    std::vector<CLI::Flag> tooManyArgumentsTestFlags;
    tooManyArgumentsTestFlags.push_back(CLI::Flag('a', "aaaaa", "test flag"));

    std::vector<CLI::Option> tooManyArgumentsTestOptions;
    tooManyArgumentsTestOptions.push_back(CLI::Option('b', "bbbbb", "b", "test argument"));
    tooManyArgumentsTestOptions.push_back(CLI::Option('c', "ccccc", "c", "test argument"));
    tooManyArgumentsTestOptions.push_back(CLI::Option('d', "ddddd", "d", "test argument"));

    std::vector<CLI::Argument> tooManyArgumentsTestArguments;
    tooManyArgumentsTestArguments.push_back(CLI::Argument("argument0", "test argument"));

    const char** tooManyArgumentsTestParameterList = new const char*[10];
    tooManyArgumentsTestParameterList[0] = "test";
    tooManyArgumentsTestParameterList[1] = "arg_0";
    tooManyArgumentsTestParameterList[2] = "arg_1";
    tooManyArgumentsTestParameterList[3] = "-a";//
    tooManyArgumentsTestParameterList[4] = "-b";
    tooManyArgumentsTestParameterList[5] = "1";
    tooManyArgumentsTestParameterList[6] = "-c5";
    tooManyArgumentsTestParameterList[7] = "--ddddd";
    tooManyArgumentsTestParameterList[8] = "ddd";
    tooManyArgumentsTestParameterList[9] = "arg_2";

    int tooManyArgumentsStandaloneTestParameterCount = 3;
    CLI::ArgumentsParser tooManyArgumentsStandaloneTestParser = CLI::ArgumentsParser(tooManyArgumentsStandaloneTestParameterCount, tooManyArgumentsTestParameterList, tooManyArgumentsTestFlags, tooManyArgumentsTestOptions, tooManyArgumentsTestArguments);

    ASSERT(tooManyArgumentsStandaloneTestParser.getArgument(0).isError);
    ASSERT(tooManyArgumentsStandaloneTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooManyArgumentsStandaloneTestParser.getArgument(0).value == "arg_0");
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('b').isError);
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('b').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('b').value == "");
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('c').isError);
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('c').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('c').value == "");
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('d').isError);
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('d').errorCode == ErrorCodes::NOT_PRESENT);
    ASSERT(tooManyArgumentsStandaloneTestParser.getOption('d').value == "");
    ASSERT(tooManyArgumentsStandaloneTestParser.wrongUsageMessages.size() == 1);
    ASSERT(tooManyArgumentsStandaloneTestParser.wrongUsageMessages.at(0) == "Too many arguments! Unexpected encounter of: arg_1");

    int tooManyArgumentsMixedTestParameterCount = 10;
    CLI::ArgumentsParser tooManyArgumentsMixedTestParser = CLI::ArgumentsParser(tooManyArgumentsMixedTestParameterCount, tooManyArgumentsTestParameterList, tooManyArgumentsTestFlags, tooManyArgumentsTestOptions, tooManyArgumentsTestArguments);

    ASSERT(tooManyArgumentsMixedTestParser.getArgument(0).isError);
    ASSERT(tooManyArgumentsMixedTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooManyArgumentsMixedTestParser.getArgument(0).value == "arg_0");
    ASSERT(tooManyArgumentsMixedTestParser.getOption('b').isError);
    ASSERT(tooManyArgumentsMixedTestParser.getOption('b').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooManyArgumentsMixedTestParser.getOption('b').value == "1");
    ASSERT(tooManyArgumentsMixedTestParser.getOption('c').isError);
    ASSERT(tooManyArgumentsMixedTestParser.getOption('c').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooManyArgumentsMixedTestParser.getOption('c').value == "5");
    ASSERT(tooManyArgumentsMixedTestParser.getOption('d').isError);
    ASSERT(tooManyArgumentsMixedTestParser.getOption('d').errorCode == ErrorCodes::WRONG_USAGE);
    ASSERT(tooManyArgumentsMixedTestParser.getOption('d').value == "ddd");
    ASSERT(tooManyArgumentsMixedTestParser.wrongUsageMessages.size() == 2);
    ASSERT(tooManyArgumentsMixedTestParser.wrongUsageMessages.at(0) == "Too many arguments! Unexpected encounter of: arg_1");
    ASSERT(tooManyArgumentsMixedTestParser.wrongUsageMessages.at(1) == "Too many arguments! Unexpected encounter of: arg_2");

    delete[] tooManyArgumentsTestParameterList;
    std::cout << "Passed too many arguments test." << std::endl;

    std::cout << "################################################################################" << std::endl;
    std::cout << "CLI argument parser help tests" << std::endl;
    std::cout << "################################################################################" << std::endl;

    // normal usage with all types of CLI input ########################
    std::vector<CLI::Flag> helpTestFlags;
    helpTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
    helpTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));

    std::vector<CLI::Option> helpTestOptions;
    helpTestOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
    helpTestOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));

    std::vector<CLI::Argument> helpTestArguments;
    helpTestArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
    helpTestArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));

    const char** helpTestCommand = new const char*[1];
    helpTestCommand[0] = "universecreator";
    CLI::ArgumentsParser helpTestParser = CLI::ArgumentsParser(1, helpTestCommand, helpTestFlags, helpTestOptions, helpTestArguments, "Create a universe with a banana and an apple.");

    ASSERT(helpTestParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
    std::cout << "Passed normal usage test." << std::endl;

    // no description ##################################################
    std::vector<CLI::Flag> usageOnlyTestFlags;
    usageOnlyTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
    usageOnlyTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));

    std::vector<CLI::Option> usageOnlyTestOptions;
    usageOnlyTestOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
    usageOnlyTestOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));

    std::vector<CLI::Argument> usageOnlyTestArguments;
    usageOnlyTestArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
    usageOnlyTestArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));

    const char** usageOnlyTestCommand = new const char*[1];
    usageOnlyTestCommand[0] = "universecreator";
    CLI::ArgumentsParser usageOnlyTestParser = CLI::ArgumentsParser(1, usageOnlyTestCommand, usageOnlyTestFlags, usageOnlyTestOptions, usageOnlyTestArguments);

    ASSERT(usageOnlyTestParser.getUsage() == "Usage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
    std::cout << "Passed normal usage without description test." << std::endl;

    // no flags ########################################################
    std::vector<CLI::Flag> noFlagsFlags;

    std::vector<CLI::Option> noFlagsOptions;
    noFlagsOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
    noFlagsOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));

    std::vector<CLI::Argument> noFlagsArguments;
    noFlagsArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
    noFlagsArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));

    const char** noFlagsCommand = new const char*[1];
    noFlagsCommand[0] = "universecreator";
    CLI::ArgumentsParser noFlagsParser = CLI::ArgumentsParser(1, noFlagsCommand, noFlagsFlags, noFlagsOptions, noFlagsArguments, "Create a universe with a banana and an apple.");

    ASSERT(noFlagsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");

    // no options ######################################################
    std::vector<CLI::Flag> noOptionsFlags;
    noOptionsFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
    noOptionsFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));

    std::vector<CLI::Option> noOptionsOptions;

    std::vector<CLI::Argument> noOptionsArguments;
    noOptionsArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
    noOptionsArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));

    const char** noOptionsCommand = new const char*[1];
    noOptionsCommand[0] = "universecreator";
    CLI::ArgumentsParser noOptionsParser = CLI::ArgumentsParser(1, noOptionsCommand, noOptionsFlags, noOptionsOptions, noOptionsArguments, "Create a universe with a banana and an apple.");

    ASSERT(noOptionsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");

    // no arguments ####################################################
    std::vector<CLI::Flag> noArgumentsFlags;
    noArgumentsFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
    noArgumentsFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));

    std::vector<CLI::Option> noArgumentsOptions;
    noArgumentsOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
    noArgumentsOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));

    std::vector<CLI::Argument> noArgumentsArguments;

    const char** noArgumentsCommand = new const char*[1];
    noArgumentsCommand[0] = "universecreator";
    CLI::ArgumentsParser noArgumentsParser = CLI::ArgumentsParser(1, noArgumentsCommand, noArgumentsFlags, noArgumentsOptions, noArgumentsArguments, "Create a universe with a banana and an apple.");

    ASSERT(noArgumentsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n");
    std::cout << "Passed absent section usage test." << std::endl;

    // no CLI input ####################################################
    std::vector<CLI::Flag> noCLIFlags;

    std::vector<CLI::Option> noCLIOptions;

    std::vector<CLI::Argument> noCLIArguments;

    const char** noCLICommand = new const char*[1];
    noCLICommand[0] = "universecreator";
    CLI::ArgumentsParser noCLIParser = CLI::ArgumentsParser(1, noCLICommand, noCLIFlags, noCLIOptions, noCLIArguments, "Create a universe with a banana and an apple.");

    ASSERT(noCLIParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator \n");
    std::cout << "Passed no CLI input usage test." << std::endl;

    // additional info test ############################################
    std::vector<CLI::Flag> additionalInfoCLIFlags;

    std::vector<CLI::Option> additionalInfoCLIOptions;

    std::vector<CLI::Argument> additionalInfoCLIArguments;

    const char** additionalInfoCLICommand = new const char*[1];
    additionalInfoCLICommand[0] = "universecreator";
    CLI::ArgumentsParser additionalInfoCLIParser = CLI::ArgumentsParser(1, additionalInfoCLICommand, additionalInfoCLIFlags, additionalInfoCLIOptions, additionalInfoCLIArguments, "Create a universe with a banana and an apple.", "Rick Astley was here.");

    ASSERT(additionalInfoCLIParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator \n\nAdditional Info:\n\n\tRick Astley was here.\n");
    std::cout << "Passed additional info test." << std::endl;

    return 0;
}
