mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
This adds a round trip test for config file generation to the fuzzer. (the next step after this PR will be a fuzzer that verifies that the round trip actually matches the results. This change ended up requiring quite a few minor changes to fix the ambiguities between the config file generation and config file reader. 1). There was a number of potential conflicts between positional names and regular option names that could be triggered in config files, this required a number of additional checks on the positional naming to ensure no conflicts. 2). flag options with disable flag override can produce output results that are not valid by themselves, resolving this required flag input to be able to handle an array and output the original value set of results. 3). strings with non-printable characters could cause all sorts of chaos in the config files. This was resolved by generating a binary string conversion format and handling multiline comments and characters, and handling escaped characters. Note; I think a better solution is to move to fully supporting string formatting and escaping along with the binary strings from TOML now that TOML 1.0 is finalized. That will not be this PR though, maybe the next one. 4). Lot of ambiguities and edge cases in the string splitter, this was reworked 5). handling of comments was not done well, especially comment characters in the name of the option which is allowed. 6). non printable characters in the option naming. This would be weird in practice but it also cause some big holes in the config file generation, so the restricted character set for option naming was expanded. (don't allow spaces or control characters). --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1499 lines
53 KiB
C++
1499 lines
53 KiB
C++
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
|
// under NSF AWARD 1414736 and by the respective contributors.
|
|
// All rights reserved.
|
|
//
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include "app_helper.hpp"
|
|
|
|
#include <cmath>
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <complex>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
#include <limits>
|
|
#include <map>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
class NotStreamable {};
|
|
|
|
class Streamable {};
|
|
|
|
std::ostream &operator<<(std::ostream &out, const Streamable &) { return out << "Streamable"; }
|
|
|
|
TEST_CASE("TypeTools: Streaming", "[helpers]") {
|
|
|
|
CHECK(CLI::detail::to_string(NotStreamable{}).empty());
|
|
|
|
CHECK("Streamable" == CLI::detail::to_string(Streamable{}));
|
|
|
|
CHECK("5" == CLI::detail::to_string(5));
|
|
|
|
CHECK(std::string("string") == CLI::detail::to_string("string"));
|
|
CHECK(std::string("string") == CLI::detail::to_string(std::string("string")));
|
|
}
|
|
|
|
TEST_CASE("TypeTools: tuple", "[helpers]") {
|
|
CHECK_FALSE(CLI::detail::is_tuple_like<int>::value);
|
|
CHECK_FALSE(CLI::detail::is_tuple_like<std::vector<double>>::value);
|
|
auto v = CLI::detail::is_tuple_like<std::tuple<double, int>>::value;
|
|
CHECK(v);
|
|
v = CLI::detail::is_tuple_like<std::tuple<double, double, double>>::value;
|
|
CHECK(v);
|
|
}
|
|
|
|
TEST_CASE("TypeTools: type_size", "[helpers]") {
|
|
auto V = CLI::detail::type_count<int>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::type_count<void>::value;
|
|
CHECK(0 == V);
|
|
V = CLI::detail::type_count<std::vector<double>>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::type_count<std::tuple<double, int>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count<std::tuple<std::string, double, int>>::value;
|
|
CHECK(3 == V);
|
|
V = CLI::detail::type_count<std::array<std::string, 5>>::value;
|
|
CHECK(5 == V);
|
|
V = CLI::detail::type_count<std::vector<std::pair<std::string, double>>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count<std::tuple<std::pair<std::string, double>>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count<std::tuple<int, std::pair<std::string, double>>>::value;
|
|
CHECK(3 == V);
|
|
V = CLI::detail::type_count<std::tuple<std::pair<int, double>, std::pair<std::string, double>>>::value;
|
|
CHECK(4 == V);
|
|
// maps
|
|
V = CLI::detail::type_count<std::map<int, std::pair<int, double>>>::value;
|
|
CHECK(3 == V);
|
|
// three level tuples
|
|
V = CLI::detail::type_count<std::tuple<int, std::pair<int, std::tuple<int, double, std::string>>>>::value;
|
|
CHECK(5 == V);
|
|
V = CLI::detail::type_count<std::pair<int, std::vector<int>>>::value;
|
|
CHECK(CLI::detail::expected_max_vector_size <= V);
|
|
V = CLI::detail::type_count<std::vector<std::vector<int>>>::value;
|
|
CHECK(CLI::detail::expected_max_vector_size == V);
|
|
}
|
|
|
|
TEST_CASE("TypeTools: type_size_min", "[helpers]") {
|
|
auto V = CLI::detail::type_count_min<int>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::type_count_min<void>::value;
|
|
CHECK(0 == V);
|
|
V = CLI::detail::type_count_min<std::vector<double>>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::type_count_min<std::tuple<double, int>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count_min<std::tuple<std::string, double, int>>::value;
|
|
CHECK(3 == V);
|
|
V = CLI::detail::type_count_min<std::array<std::string, 5>>::value;
|
|
CHECK(5 == V);
|
|
V = CLI::detail::type_count_min<std::vector<std::pair<std::string, double>>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count_min<std::tuple<std::pair<std::string, double>>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count_min<std::tuple<int, std::pair<std::string, double>>>::value;
|
|
CHECK(3 == V);
|
|
V = CLI::detail::type_count_min<std::tuple<std::pair<int, double>, std::pair<std::string, double>>>::value;
|
|
CHECK(4 == V);
|
|
// maps
|
|
V = CLI::detail::type_count_min<std::map<int, std::pair<int, double>>>::value;
|
|
CHECK(3 == V);
|
|
// three level tuples
|
|
V = CLI::detail::type_count_min<std::tuple<int, std::pair<int, std::tuple<int, double, std::string>>>>::value;
|
|
CHECK(5 == V);
|
|
V = CLI::detail::type_count_min<std::pair<int, std::vector<int>>>::value;
|
|
CHECK(2 == V);
|
|
V = CLI::detail::type_count_min<std::vector<std::vector<int>>>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::type_count_min<std::vector<std::vector<std::pair<int, int>>>>::value;
|
|
CHECK(2 == V);
|
|
}
|
|
|
|
TEST_CASE("TypeTools: expected_count", "[helpers]") {
|
|
auto V = CLI::detail::expected_count<int>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::expected_count<void>::value;
|
|
CHECK(0 == V);
|
|
V = CLI::detail::expected_count<std::vector<double>>::value;
|
|
CHECK(CLI::detail::expected_max_vector_size == V);
|
|
V = CLI::detail::expected_count<std::tuple<double, int>>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::expected_count<std::tuple<std::string, double, int>>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::expected_count<std::array<std::string, 5>>::value;
|
|
CHECK(1 == V);
|
|
V = CLI::detail::expected_count<std::vector<std::pair<std::string, double>>>::value;
|
|
CHECK(CLI::detail::expected_max_vector_size == V);
|
|
}
|
|
|
|
TEST_CASE("Split: SimpleByToken", "[helpers]") {
|
|
auto out = CLI::detail::split("one.two.three", '.');
|
|
REQUIRE(out.size() == 3u);
|
|
CHECK(out.at(0) == "one");
|
|
CHECK(out.at(1) == "two");
|
|
CHECK(out.at(2) == "three");
|
|
}
|
|
|
|
TEST_CASE("Split: Single", "[helpers]") {
|
|
auto out = CLI::detail::split("one", '.');
|
|
REQUIRE(out.size() == 1u);
|
|
CHECK(out.at(0) == "one");
|
|
}
|
|
|
|
TEST_CASE("Split: Empty", "[helpers]") {
|
|
auto out = CLI::detail::split("", '.');
|
|
REQUIRE(out.size() == 1u);
|
|
CHECK(out.at(0).empty());
|
|
}
|
|
|
|
TEST_CASE("String: InvalidName", "[helpers]") {
|
|
CHECK(CLI::detail::valid_name_string("valid"));
|
|
CHECK_FALSE(CLI::detail::valid_name_string("-invalid"));
|
|
CHECK(CLI::detail::valid_name_string("va-li-d"));
|
|
CHECK_FALSE(CLI::detail::valid_name_string("valid{}"));
|
|
CHECK(CLI::detail::valid_name_string("_valid"));
|
|
CHECK(CLI::detail::valid_name_string("/valid"));
|
|
CHECK(CLI::detail::valid_name_string("vali?d"));
|
|
CHECK(CLI::detail::valid_name_string("@@@@"));
|
|
CHECK(CLI::detail::valid_name_string("b@d2?"));
|
|
CHECK(CLI::detail::valid_name_string("2vali?d"));
|
|
CHECK_FALSE(CLI::detail::valid_name_string("!valid"));
|
|
}
|
|
|
|
TEST_CASE("StringTools: Modify", "[helpers]") {
|
|
int cnt{0};
|
|
std::string newString = CLI::detail::find_and_modify("======", "=", [&cnt](std::string &str, std::size_t index) {
|
|
if((++cnt) % 2 == 0) {
|
|
str[index] = ':';
|
|
}
|
|
return index + 1;
|
|
});
|
|
CHECK("=:=:=:" == newString);
|
|
}
|
|
|
|
TEST_CASE("StringTools: Modify2", "[helpers]") {
|
|
std::string newString =
|
|
CLI::detail::find_and_modify("this is a string test", "is", [](std::string &str, std::size_t index) {
|
|
if((index > 1) && (str[index - 1] != ' ')) {
|
|
str[index] = 'a';
|
|
str[index + 1] = 't';
|
|
}
|
|
return index + 1;
|
|
});
|
|
CHECK("that is a string test" == newString);
|
|
}
|
|
|
|
TEST_CASE("StringTools: Modify3", "[helpers]") {
|
|
// this picks up 3 sets of 3 after the 'b' then collapses the new first set
|
|
std::string newString = CLI::detail::find_and_modify("baaaaaaaaaa", "aaa", [](std::string &str, std::size_t index) {
|
|
str.erase(index, 3);
|
|
str.insert(str.begin(), 'a');
|
|
return 0u;
|
|
});
|
|
CHECK("aba" == newString);
|
|
}
|
|
|
|
TEST_CASE("StringTools: flagValues", "[helpers]") {
|
|
errno = 0;
|
|
CHECK(-1 == CLI::detail::to_flag_value("0"));
|
|
CHECK(errno == 0);
|
|
CHECK(1 == CLI::detail::to_flag_value("t"));
|
|
CHECK(1 == CLI::detail::to_flag_value("1"));
|
|
CHECK(6 == CLI::detail::to_flag_value("6"));
|
|
CHECK(-6 == CLI::detail::to_flag_value("-6"));
|
|
CHECK(-1 == CLI::detail::to_flag_value("false"));
|
|
CHECK(1 == CLI::detail::to_flag_value("YES"));
|
|
errno = 0;
|
|
CLI::detail::to_flag_value("frog");
|
|
CHECK(errno == EINVAL);
|
|
errno = 0;
|
|
CLI::detail::to_flag_value("q");
|
|
CHECK(errno == EINVAL);
|
|
errno = 0;
|
|
CLI::detail::to_flag_value(
|
|
"77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777");
|
|
CHECK(errno == ERANGE);
|
|
errno = 0;
|
|
CHECK(-1 == CLI::detail::to_flag_value("NO"));
|
|
CHECK(475555233 == CLI::detail::to_flag_value("475555233"));
|
|
}
|
|
|
|
TEST_CASE("StringTools: Validation", "[helpers]") {
|
|
CHECK(CLI::detail::isalpha(""));
|
|
CHECK(CLI::detail::isalpha("a"));
|
|
CHECK(CLI::detail::isalpha("abcd"));
|
|
CHECK_FALSE(CLI::detail::isalpha("_"));
|
|
CHECK_FALSE(CLI::detail::isalpha("2"));
|
|
CHECK_FALSE(CLI::detail::isalpha("test test"));
|
|
CHECK_FALSE(CLI::detail::isalpha("test "));
|
|
CHECK_FALSE(CLI::detail::isalpha(" test"));
|
|
CHECK_FALSE(CLI::detail::isalpha("test2"));
|
|
}
|
|
|
|
TEST_CASE("StringTools: binaryEscapseConversion", "[helpers]") {
|
|
std::string testString("string1");
|
|
std::string estring = CLI::detail::binary_escape_string(testString);
|
|
CHECK(testString == estring);
|
|
CHECK_FALSE(CLI::detail::is_binary_escaped_string(estring));
|
|
|
|
std::string testString2("\nstring1\n");
|
|
estring = CLI::detail::binary_escape_string(testString2);
|
|
CHECK_FALSE(testString == estring);
|
|
CHECK(CLI::detail::is_binary_escaped_string(estring));
|
|
std::string rstring = CLI::detail::extract_binary_string(estring);
|
|
CHECK(rstring == testString2);
|
|
|
|
testString2.push_back(0);
|
|
testString2.push_back(static_cast<char>(197));
|
|
testString2.push_back(78);
|
|
testString2.push_back(-34);
|
|
|
|
rstring = CLI::detail::extract_binary_string(CLI::detail::binary_escape_string(testString2));
|
|
CHECK(rstring == testString2);
|
|
|
|
testString2.push_back('b');
|
|
testString2.push_back('G');
|
|
|
|
rstring = CLI::detail::extract_binary_string(CLI::detail::binary_escape_string(testString2));
|
|
CHECK(rstring == testString2);
|
|
auto rstring2 = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(rstring == rstring2);
|
|
}
|
|
|
|
TEST_CASE("StringTools: binaryStrings", "[helpers]") {
|
|
std::string rstring = "B\"()\"";
|
|
CHECK(CLI::detail::extract_binary_string(rstring).empty());
|
|
|
|
rstring = "B\"(\\x35\\xa7)\"";
|
|
auto result = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(result[0] == static_cast<char>(0x35));
|
|
CHECK(result[1] == static_cast<char>(0xa7));
|
|
|
|
rstring = "B\"(\\x3e\\xf7)\"";
|
|
result = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(result[0] == static_cast<char>(0x3e));
|
|
CHECK(result[1] == static_cast<char>(0xf7));
|
|
|
|
rstring = "B\"(\\x3E\\xf7)\"";
|
|
result = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(result[0] == static_cast<char>(0x3e));
|
|
CHECK(result[1] == static_cast<char>(0xf7));
|
|
|
|
rstring = "B\"(\\X3E\\XF7)\"";
|
|
result = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(result[0] == static_cast<char>(0x3e));
|
|
CHECK(result[1] == static_cast<char>(0xf7));
|
|
|
|
rstring = "B\"(\\XME\\XK7)\"";
|
|
result = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(result == "\\XME\\XK7");
|
|
|
|
rstring = "B\"(\\XEM\\X7K)\"";
|
|
result = CLI::detail::extract_binary_string(rstring);
|
|
CHECK(result == "\\XEM\\X7K");
|
|
}
|
|
|
|
TEST_CASE("StringTools: escapeConversion", "[helpers]") {
|
|
CHECK(CLI::detail::remove_escaped_characters("test\\\"") == "test\"");
|
|
CHECK(CLI::detail::remove_escaped_characters("test\\}") == "test}");
|
|
CHECK(CLI::detail::remove_escaped_characters("test\\\\") == "test\\");
|
|
CHECK(CLI::detail::remove_escaped_characters("test\\\\") == "test\\");
|
|
CHECK(CLI::detail::remove_escaped_characters("test\\k") == "test\\k");
|
|
}
|
|
|
|
TEST_CASE("Trim: Various", "[helpers]") {
|
|
std::string s1{" sdlfkj sdflk sd s "};
|
|
std::string a1{"sdlfkj sdflk sd s"};
|
|
CLI::detail::trim(s1);
|
|
CHECK(s1 == a1);
|
|
|
|
std::string s2{" a \t"};
|
|
CLI::detail::trim(s2);
|
|
CHECK(s2 == "a");
|
|
|
|
std::string s3{" a \n"};
|
|
CLI::detail::trim(s3);
|
|
CHECK(s3 == "a");
|
|
|
|
std::string s4{" a b "};
|
|
CHECK(CLI::detail::trim(s4) == "a b");
|
|
}
|
|
|
|
TEST_CASE("Trim: VariousFilters", "[helpers]") {
|
|
std::string s1{" sdlfkj sdflk sd s "};
|
|
std::string a1{"sdlfkj sdflk sd s"};
|
|
CLI::detail::trim(s1, " ");
|
|
CHECK(s1 == a1);
|
|
|
|
std::string s2{" a \t"};
|
|
CLI::detail::trim(s2, " ");
|
|
CHECK(s2 == "a \t");
|
|
|
|
std::string s3{"abdavda"};
|
|
CLI::detail::trim(s3, "a");
|
|
CHECK(s3 == "bdavd");
|
|
|
|
std::string s4{"abcabcabc"};
|
|
CHECK(CLI::detail::trim(s4, "ab") == "cabcabc");
|
|
}
|
|
|
|
TEST_CASE("Trim: TrimCopy", "[helpers]") {
|
|
std::string orig{" cabc "};
|
|
std::string trimmed = CLI::detail::trim_copy(orig);
|
|
CHECK(trimmed == "cabc");
|
|
CHECK(trimmed != orig);
|
|
CLI::detail::trim(orig);
|
|
CHECK(orig == trimmed);
|
|
|
|
orig = "abcabcabc";
|
|
trimmed = CLI::detail::trim_copy(orig, "ab");
|
|
CHECK(trimmed == "cabcabc");
|
|
CHECK(trimmed != orig);
|
|
CLI::detail::trim(orig, "ab");
|
|
CHECK(orig == trimmed);
|
|
}
|
|
|
|
TEST_CASE("Validators: FileExists", "[helpers]") {
|
|
std::string myfile{"TestFileNotUsed.txt"};
|
|
CHECK_FALSE(CLI::ExistingFile(myfile).empty());
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
CHECK(CLI::ExistingFile(myfile).empty());
|
|
|
|
std::remove(myfile.c_str());
|
|
CHECK_FALSE(CLI::ExistingFile(myfile).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: FileNotExists", "[helpers]") {
|
|
std::string myfile{"TestFileNotUsed.txt"};
|
|
CHECK(CLI::NonexistentPath(myfile).empty());
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
CHECK_FALSE(CLI::NonexistentPath(myfile).empty());
|
|
|
|
std::remove(myfile.c_str());
|
|
CHECK(CLI::NonexistentPath(myfile).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: FilePathModifier", "[helpers]") {
|
|
std::string myfile{"../TestFileNotUsed_1.txt"};
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
std::string filename = "TestFileNotUsed_1.txt";
|
|
CLI::FileOnDefaultPath defPath("../");
|
|
CHECK(defPath(filename).empty());
|
|
CHECK(filename == myfile);
|
|
std::string filename2 = "nonexistingfile.csv";
|
|
CHECK_FALSE(defPath(filename2).empty());
|
|
// check it didn't modify the string
|
|
CHECK(filename2 == "nonexistingfile.csv");
|
|
CHECK(defPath(filename).empty());
|
|
std::remove(myfile.c_str());
|
|
CHECK_FALSE(defPath(myfile).empty());
|
|
// now test the no error version
|
|
CLI::FileOnDefaultPath defPathNoFail("../", false);
|
|
CHECK(defPathNoFail(filename2).empty());
|
|
CHECK(filename2 == "nonexistingfile.csv");
|
|
}
|
|
|
|
TEST_CASE("Validators: FileIsDir", "[helpers]") {
|
|
std::string mydir{"../tests"};
|
|
CHECK(!CLI::ExistingFile(mydir).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: DirectoryExists", "[helpers]") {
|
|
std::string mydir{"../tests"};
|
|
CHECK(CLI::ExistingDirectory(mydir).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: DirectoryNotExists", "[helpers]") {
|
|
std::string mydir{"nondirectory"};
|
|
CHECK(!CLI::ExistingDirectory(mydir).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: DirectoryIsFile", "[helpers]") {
|
|
std::string myfile{"TestFileNotUsed.txt"};
|
|
CHECK(CLI::NonexistentPath(myfile).empty());
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
CHECK_FALSE(CLI::ExistingDirectory(myfile).empty());
|
|
|
|
std::remove(myfile.c_str());
|
|
CHECK(CLI::NonexistentPath(myfile).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: PathExistsDir", "[helpers]") {
|
|
std::string mydir{"../tests"};
|
|
CHECK(CLI::ExistingPath(mydir).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: PathExistsFile", "[helpers]") {
|
|
std::string myfile{"TestFileNotUsed.txt"};
|
|
CHECK_FALSE(CLI::ExistingPath(myfile).empty());
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
CHECK(CLI::ExistingPath(myfile).empty());
|
|
|
|
std::remove(myfile.c_str());
|
|
CHECK_FALSE(CLI::ExistingPath(myfile).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: PathNotExistsDir", "[helpers]") {
|
|
std::string mydir{"nonpath"};
|
|
CHECK(!CLI::ExistingPath(mydir).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: IPValidate1", "[helpers]") {
|
|
std::string ip = "1.1.1.1";
|
|
CHECK(CLI::ValidIPV4(ip).empty());
|
|
ip = "224.255.0.1";
|
|
CHECK(CLI::ValidIPV4(ip).empty());
|
|
ip = "-1.255.0.1";
|
|
CHECK_FALSE(CLI::ValidIPV4(ip).empty());
|
|
ip = "1.256.0.1";
|
|
CHECK_FALSE(CLI::ValidIPV4(ip).empty());
|
|
ip = "1.256.0.1";
|
|
CHECK_FALSE(CLI::ValidIPV4(ip).empty());
|
|
ip = "aaa";
|
|
CHECK_FALSE(CLI::ValidIPV4(ip).empty());
|
|
ip = "1.2.3.abc";
|
|
CHECK_FALSE(CLI::ValidIPV4(ip).empty());
|
|
ip = "11.22";
|
|
CHECK_FALSE(CLI::ValidIPV4(ip).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: PositiveValidator", "[helpers]") {
|
|
std::string num = "1.1.1.1";
|
|
CHECK_FALSE(CLI::PositiveNumber(num).empty());
|
|
num = "1";
|
|
CHECK(CLI::PositiveNumber(num).empty());
|
|
num = "10000";
|
|
CHECK(CLI::PositiveNumber(num).empty());
|
|
num = "0";
|
|
CHECK_FALSE(CLI::PositiveNumber(num).empty());
|
|
num = "+0.5";
|
|
CHECK(CLI::PositiveNumber(num).empty());
|
|
num = "-1";
|
|
CHECK_FALSE(CLI::PositiveNumber(num).empty());
|
|
num = "-1.5";
|
|
CHECK_FALSE(CLI::PositiveNumber(num).empty());
|
|
num = "a";
|
|
CHECK_FALSE(CLI::PositiveNumber(num).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: NonNegativeValidator", "[helpers]") {
|
|
std::string num = "1.1.1.1";
|
|
CHECK_FALSE(CLI::NonNegativeNumber(num).empty());
|
|
num = "1";
|
|
CHECK(CLI::NonNegativeNumber(num).empty());
|
|
num = "10000";
|
|
CHECK(CLI::NonNegativeNumber(num).empty());
|
|
num = "0";
|
|
CHECK(CLI::NonNegativeNumber(num).empty());
|
|
num = "+0.5";
|
|
CHECK(CLI::NonNegativeNumber(num).empty());
|
|
num = "-1";
|
|
CHECK_FALSE(CLI::NonNegativeNumber(num).empty());
|
|
num = "-1.5";
|
|
CHECK_FALSE(CLI::NonNegativeNumber(num).empty());
|
|
num = "a";
|
|
CHECK_FALSE(CLI::NonNegativeNumber(num).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: NumberValidator", "[helpers]") {
|
|
std::string num = "1.1.1.1";
|
|
CHECK_FALSE(CLI::Number(num).empty());
|
|
num = "1.7";
|
|
CHECK(CLI::Number(num).empty());
|
|
num = "10000";
|
|
CHECK(CLI::Number(num).empty());
|
|
num = "-0.000";
|
|
CHECK(CLI::Number(num).empty());
|
|
num = "+1.55";
|
|
CHECK(CLI::Number(num).empty());
|
|
num = "a";
|
|
CHECK_FALSE(CLI::Number(num).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: CombinedAndRange", "[helpers]") {
|
|
auto crange = CLI::Range(0, 12) & CLI::Range(4, 16);
|
|
CHECK(crange("4").empty());
|
|
CHECK(crange("12").empty());
|
|
CHECK(crange("7").empty());
|
|
|
|
CHECK_FALSE(crange("-2").empty());
|
|
CHECK_FALSE(crange("2").empty());
|
|
CHECK_FALSE(crange("15").empty());
|
|
CHECK_FALSE(crange("16").empty());
|
|
CHECK_FALSE(crange("18").empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: CombinedOrRange", "[helpers]") {
|
|
auto crange = CLI::Range(0, 4) | CLI::Range(8, 12);
|
|
|
|
CHECK_FALSE(crange("-2").empty());
|
|
CHECK(crange("2").empty());
|
|
CHECK_FALSE(crange("5").empty());
|
|
CHECK(crange("8").empty());
|
|
CHECK(crange("12").empty());
|
|
CHECK_FALSE(crange("16").empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: CombinedPaths", "[helpers]") {
|
|
std::string myfile{"TestFileNotUsed.txt"};
|
|
CHECK_FALSE(CLI::ExistingFile(myfile).empty());
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
|
|
std::string dir{"../tests"};
|
|
std::string notpath{"nondirectory"};
|
|
|
|
auto path_or_dir = CLI::ExistingPath | CLI::ExistingDirectory;
|
|
CHECK(path_or_dir(dir).empty());
|
|
CHECK(path_or_dir(myfile).empty());
|
|
CHECK_FALSE(path_or_dir(notpath).empty());
|
|
|
|
auto file_or_dir = CLI::ExistingFile | CLI::ExistingDirectory;
|
|
CHECK(file_or_dir(dir).empty());
|
|
CHECK(file_or_dir(myfile).empty());
|
|
CHECK_FALSE(file_or_dir(notpath).empty());
|
|
|
|
auto path_and_dir = CLI::ExistingPath & CLI::ExistingDirectory;
|
|
CHECK(path_and_dir(dir).empty());
|
|
CHECK_FALSE(path_and_dir(myfile).empty());
|
|
CHECK_FALSE(path_and_dir(notpath).empty());
|
|
|
|
auto path_and_file = CLI::ExistingFile & CLI::ExistingDirectory;
|
|
CHECK_FALSE(path_and_file(dir).empty());
|
|
CHECK_FALSE(path_and_file(myfile).empty());
|
|
CHECK_FALSE(path_and_file(notpath).empty());
|
|
|
|
std::remove(myfile.c_str());
|
|
CHECK_FALSE(CLI::ExistingFile(myfile).empty());
|
|
}
|
|
|
|
TEST_CASE("Validators: ProgramNameSplit", "[helpers]") {
|
|
TempFile myfile{"program_name1.exe"};
|
|
{
|
|
std::ofstream out{myfile};
|
|
out << "useless string doesn't matter" << std::endl;
|
|
}
|
|
auto res =
|
|
CLI::detail::split_program_name(std::string("./") + std::string(myfile) + " this is a bunch of extra stuff ");
|
|
CHECK(std::string("./") + std::string(myfile) == res.first);
|
|
CHECK("this is a bunch of extra stuff" == res.second);
|
|
|
|
TempFile myfile2{"program name1.exe"};
|
|
{
|
|
std::ofstream out{myfile2};
|
|
out << "useless string doesn't matter" << std::endl;
|
|
}
|
|
res = CLI::detail::split_program_name(std::string(" ") + std::string("./") + std::string(myfile2) +
|
|
" this is a bunch of extra stuff ");
|
|
CHECK(std::string("./") + std::string(myfile2) == res.first);
|
|
CHECK("this is a bunch of extra stuff" == res.second);
|
|
|
|
res = CLI::detail::split_program_name("./program_name this is a bunch of extra stuff ");
|
|
CHECK("./program_name" == res.first);
|
|
CHECK("this is a bunch of extra stuff" == res.second);
|
|
|
|
res = CLI::detail::split_program_name(std::string(" ./") + std::string(myfile) + " ");
|
|
CHECK(std::string("./") + std::string(myfile) == res.first);
|
|
CHECK(res.second.empty());
|
|
|
|
res = CLI::detail::split_program_name("'odd_program_name.exe --arg --arg2=5");
|
|
CHECK("'odd_program_name.exe" == res.first);
|
|
CHECK_FALSE(res.second.empty());
|
|
}
|
|
|
|
TEST_CASE("CheckedMultiply: Int", "[helpers]") {
|
|
int a{10};
|
|
int b{-20};
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(-200 == a);
|
|
|
|
a = 0;
|
|
b = -20;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(0 == a);
|
|
|
|
a = 20;
|
|
b = 0;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(0 == a);
|
|
|
|
a = (std::numeric_limits<int>::max)();
|
|
b = 1;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::max)() == a);
|
|
|
|
a = (std::numeric_limits<int>::max)();
|
|
b = 2;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::max)() == a);
|
|
|
|
a = (std::numeric_limits<int>::max)();
|
|
b = -1;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(-(std::numeric_limits<int>::max)() == a);
|
|
|
|
a = (std::numeric_limits<int>::max)();
|
|
b = (std::numeric_limits<int>::max)();
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::max)() == a);
|
|
|
|
a = (std::numeric_limits<int>::min)();
|
|
b = (std::numeric_limits<int>::max)();
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::min)() == a);
|
|
|
|
a = (std::numeric_limits<int>::min)();
|
|
b = 1;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::min)() == a);
|
|
|
|
a = (std::numeric_limits<int>::min)();
|
|
b = -1;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::min)() == a);
|
|
|
|
b = (std::numeric_limits<int>::min)();
|
|
a = -1;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(-1 == a);
|
|
|
|
a = (std::numeric_limits<int>::min)() / 100;
|
|
b = 99;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::min)() / 100 * 99 == a);
|
|
|
|
a = (std::numeric_limits<int>::min)() / 100;
|
|
b = -101;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<int>::min)() / 100 == a);
|
|
a = 2;
|
|
b = (std::numeric_limits<int>::min)() / 2;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
a = (std::numeric_limits<int>::min)() / 2;
|
|
b = 2;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
|
|
a = 4;
|
|
b = (std::numeric_limits<int>::min)() / 4;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
|
|
a = 48;
|
|
b = (std::numeric_limits<int>::min)() / 48;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
}
|
|
|
|
TEST_CASE("CheckedMultiply: SizeT", "[helpers]") {
|
|
std::size_t a = 10;
|
|
std::size_t b = 20;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(200u == a);
|
|
|
|
a = 0u;
|
|
b = 20u;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(0u == a);
|
|
|
|
a = 20u;
|
|
b = 0u;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(0u == a);
|
|
|
|
a = (std::numeric_limits<std::size_t>::max)();
|
|
b = 1u;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<std::size_t>::max)() == a);
|
|
|
|
a = (std::numeric_limits<std::size_t>::max)();
|
|
b = 2u;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<std::size_t>::max)() == a);
|
|
|
|
a = (std::numeric_limits<std::size_t>::max)();
|
|
b = (std::numeric_limits<std::size_t>::max)();
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<std::size_t>::max)() == a);
|
|
|
|
a = (std::numeric_limits<std::size_t>::max)() / 100;
|
|
b = 99u;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<std::size_t>::max)() / 100u * 99u == a);
|
|
}
|
|
|
|
TEST_CASE("CheckedMultiply: Float", "[helpers]") {
|
|
float a{10.0F};
|
|
float b{20.0F};
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(200 == Approx(a));
|
|
|
|
a = 0.0F;
|
|
b = 20.0F;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(0 == Approx(a));
|
|
|
|
a = INFINITY;
|
|
b = 20.0F;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(INFINITY == Approx(a));
|
|
|
|
a = 2.0F;
|
|
b = -INFINITY;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(-INFINITY == Approx(a));
|
|
|
|
a = (std::numeric_limits<float>::max)() / 100.0F;
|
|
b = 1.0F;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<float>::max)() / 100.0F == Approx(a));
|
|
|
|
a = (std::numeric_limits<float>::max)() / 100.0F;
|
|
b = 99.0F;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<float>::max)() / 100.0F * 99.0F == Approx(a));
|
|
|
|
a = (std::numeric_limits<float>::max)() / 100.0F;
|
|
b = 101;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<float>::max)() / 100.0F == Approx(a));
|
|
|
|
a = (std::numeric_limits<float>::max)() / 100.0F;
|
|
b = -99;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<float>::max)() / 100.0F * -99.0F == Approx(a));
|
|
|
|
a = (std::numeric_limits<float>::max)() / 100.0F;
|
|
b = -101;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<float>::max)() / 100.0F == Approx(a));
|
|
}
|
|
|
|
TEST_CASE("CheckedMultiply: Double", "[helpers]") {
|
|
double a{10.0F};
|
|
double b{20.0F};
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(200 == Approx(a));
|
|
|
|
a = 0;
|
|
b = 20;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(0 == Approx(a));
|
|
|
|
a = std::numeric_limits<double>::infinity();
|
|
b = 20;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(std::numeric_limits<double>::infinity() == Approx(a));
|
|
|
|
a = 2;
|
|
b = -std::numeric_limits<double>::infinity();
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE(-std::numeric_limits<double>::infinity() == Approx(a));
|
|
|
|
a = (std::numeric_limits<double>::max)() / 100;
|
|
b = 1;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<double>::max)() / 100 == Approx(a));
|
|
|
|
a = (std::numeric_limits<double>::max)() / 100;
|
|
b = 99;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<double>::max)() / 100 * 99 == Approx(a));
|
|
|
|
a = (std::numeric_limits<double>::max)() / 100;
|
|
b = 101;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<double>::max)() / 100 == Approx(a));
|
|
|
|
a = (std::numeric_limits<double>::max)() / 100;
|
|
b = -99;
|
|
REQUIRE(CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<double>::max)() / 100 * -99 == Approx(a));
|
|
|
|
a = (std::numeric_limits<double>::max)() / 100;
|
|
b = -101;
|
|
REQUIRE(!CLI::detail::checked_multiply(a, b));
|
|
REQUIRE((std::numeric_limits<double>::max)() / 100 == Approx(a));
|
|
}
|
|
|
|
// Yes, this is testing an app_helper :)
|
|
TEST_CASE("AppHelper: TempfileCreated", "[helpers]") {
|
|
std::string name = "TestFileNotUsed.txt";
|
|
{
|
|
TempFile myfile{name};
|
|
|
|
CHECK_FALSE(CLI::ExistingFile(myfile).empty());
|
|
|
|
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
|
CHECK(ok);
|
|
CHECK(CLI::ExistingFile(name).empty());
|
|
CHECK_THROWS_AS([&]() { TempFile otherfile(name); }(), std::runtime_error);
|
|
}
|
|
CHECK_FALSE(CLI::ExistingFile(name).empty());
|
|
}
|
|
|
|
TEST_CASE("AppHelper: TempfileNotCreated", "[helpers]") {
|
|
std::string name = "TestFileNotUsed.txt";
|
|
{
|
|
TempFile myfile{name};
|
|
|
|
CHECK_FALSE(CLI::ExistingFile(myfile).empty());
|
|
}
|
|
CHECK_FALSE(CLI::ExistingFile(name).empty());
|
|
}
|
|
|
|
TEST_CASE("AppHelper: Ofstream", "[helpers]") {
|
|
|
|
std::string name = "TestFileNotUsed.txt";
|
|
{
|
|
TempFile myfile(name);
|
|
|
|
{
|
|
std::ofstream out{myfile};
|
|
out << "this is output" << std::endl;
|
|
}
|
|
|
|
CHECK(CLI::ExistingFile(myfile).empty());
|
|
}
|
|
CHECK_FALSE(CLI::ExistingFile(name).empty());
|
|
}
|
|
|
|
TEST_CASE("Split: StringList", "[helpers]") {
|
|
|
|
std::vector<std::string> results{"a", "long", "--lone", "-q"};
|
|
CHECK(CLI::detail::split_names("a,long,--lone,-q") == results);
|
|
CHECK(CLI::detail::split_names(" a, long, --lone, -q") == results);
|
|
CHECK(CLI::detail::split_names(" a , long , --lone , -q ") == results);
|
|
CHECK(CLI::detail::split_names(" a , long , --lone , -q ") == results);
|
|
|
|
CHECK(CLI::detail::split_names("one") == std::vector<std::string>({"one"}));
|
|
}
|
|
|
|
TEST_CASE("RegEx: Shorts", "[helpers]") {
|
|
std::string name, value;
|
|
|
|
CHECK(CLI::detail::split_short("-a", name, value));
|
|
CHECK(name == "a");
|
|
CHECK(value.empty());
|
|
|
|
CHECK(CLI::detail::split_short("-B", name, value));
|
|
CHECK(name == "B");
|
|
CHECK(value.empty());
|
|
|
|
CHECK(CLI::detail::split_short("-cc", name, value));
|
|
CHECK(name == "c");
|
|
CHECK(value == "c");
|
|
|
|
CHECK(CLI::detail::split_short("-simple", name, value));
|
|
CHECK(name == "s");
|
|
CHECK(value == "imple");
|
|
|
|
CHECK_FALSE(CLI::detail::split_short("--a", name, value));
|
|
CHECK_FALSE(CLI::detail::split_short("--thing", name, value));
|
|
CHECK_FALSE(CLI::detail::split_short("--", name, value));
|
|
CHECK_FALSE(CLI::detail::split_short("something", name, value));
|
|
CHECK_FALSE(CLI::detail::split_short("s", name, value));
|
|
}
|
|
|
|
TEST_CASE("RegEx: Longs", "[helpers]") {
|
|
std::string name, value;
|
|
|
|
CHECK(CLI::detail::split_long("--a", name, value));
|
|
CHECK(name == "a");
|
|
CHECK(value.empty());
|
|
|
|
CHECK(CLI::detail::split_long("--thing", name, value));
|
|
CHECK(name == "thing");
|
|
CHECK(value.empty());
|
|
|
|
CHECK(CLI::detail::split_long("--some=thing", name, value));
|
|
CHECK(name == "some");
|
|
CHECK(value == "thing");
|
|
|
|
CHECK_FALSE(CLI::detail::split_long("-a", name, value));
|
|
CHECK_FALSE(CLI::detail::split_long("-things", name, value));
|
|
CHECK_FALSE(CLI::detail::split_long("Q", name, value));
|
|
CHECK_FALSE(CLI::detail::split_long("--", name, value));
|
|
}
|
|
|
|
TEST_CASE("RegEx: SplittingNew", "[helpers]") {
|
|
|
|
std::vector<std::string> shorts;
|
|
std::vector<std::string> longs;
|
|
std::string pname;
|
|
|
|
CHECK_NOTHROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--long", "-s", "-q", "--also-long"}));
|
|
CHECK(longs == std::vector<std::string>({"long", "also-long"}));
|
|
CHECK(shorts == std::vector<std::string>({"s", "q"}));
|
|
CHECK(pname.empty());
|
|
|
|
std::tie(shorts, longs, pname) = CLI::detail::get_names({"--long", "", "-s", "-q", "", "--also-long"});
|
|
CHECK(longs == std::vector<std::string>({"long", "also-long"}));
|
|
CHECK(shorts == std::vector<std::string>({"s", "q"}));
|
|
|
|
CHECK_THROWS_AS([&]() { std::tie(shorts, longs, pname) = CLI::detail::get_names({"-"}); }(), CLI::BadNameString);
|
|
CHECK_THROWS_AS([&]() { std::tie(shorts, longs, pname) = CLI::detail::get_names({"--"}); }(), CLI::BadNameString);
|
|
CHECK_THROWS_AS([&]() { std::tie(shorts, longs, pname) = CLI::detail::get_names({"-hi"}); }(), CLI::BadNameString);
|
|
CHECK_THROWS_AS([&]() { std::tie(shorts, longs, pname) = CLI::detail::get_names({"---hi"}); }(),
|
|
CLI::BadNameString);
|
|
CHECK_THROWS_AS(
|
|
[&]() {
|
|
std::tie(shorts, longs, pname) = CLI::detail::get_names({"one", "two"});
|
|
}(),
|
|
CLI::BadNameString);
|
|
}
|
|
|
|
TEST_CASE("String: ToLower", "[helpers]") { CHECK("one and two" == CLI::detail::to_lower("one And TWO")); }
|
|
|
|
TEST_CASE("Join: Forward", "[helpers]") {
|
|
std::vector<std::string> val{{"one", "two", "three"}};
|
|
CHECK(CLI::detail::join(val) == "one,two,three");
|
|
CHECK(CLI::detail::join(val, ";") == "one;two;three");
|
|
}
|
|
|
|
TEST_CASE("Join: Backward", "[helpers]") {
|
|
std::vector<std::string> val{{"three", "two", "one"}};
|
|
CHECK(CLI::detail::rjoin(val) == "one,two,three");
|
|
CHECK(CLI::detail::rjoin(val, ";") == "one;two;three");
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Simple", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "two three"};
|
|
std::string orig{R"(one "two three")"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: SimpleDifferentQuotes", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "two three"};
|
|
std::string orig{R"(one `two three`)"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: SimpleMissingQuotes", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "two three"};
|
|
std::string orig{R"(one `two three)"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: SimpleMissingQuotesEscaped", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "two three`"};
|
|
std::string orig{R"(one `two three\`)"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: SimpleDifferentQuotes2", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "two three"};
|
|
std::string orig{R"(one 'two three')"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Bracket1", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "[two, three]"};
|
|
std::string orig{"one, [two, three]"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig, ',', false);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Bracket2", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "<two, three>"};
|
|
std::string orig{"one, <two, three>"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig, ',', false);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Bracket3", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "(two, three)"};
|
|
std::string orig{"one, (two, three)"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig, ',', false);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Bracket4", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", "{two, three}"};
|
|
std::string orig{"one, {two, three}"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig, ',', false);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Comment", "[helpers]") {
|
|
std::vector<std::string> oput = {R"(["quote1", "#"])"};
|
|
std::string orig{R"(["quote1", "#"])"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig, '#', false);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Layered", "[helpers]") {
|
|
std::vector<std::string> output = {R"(one 'two three')"};
|
|
std::string orig{R"("one 'two three'")"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == output);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: Spaces", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", " two three"};
|
|
std::string orig{R"( one " two three" )"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("SplitUp: BadStrings", "[helpers]") {
|
|
std::vector<std::string> oput = {"one", " two three"};
|
|
std::string orig{R"( one " two three )"};
|
|
std::vector<std::string> result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
|
|
oput = {"one", " two three"};
|
|
orig = R"( one ' two three )";
|
|
result = CLI::detail::split_up(orig);
|
|
CHECK(result == oput);
|
|
}
|
|
|
|
TEST_CASE("Types: TypeName", "[helpers]") {
|
|
std::string int_name = CLI::detail::type_name<int>();
|
|
CHECK(int_name == "INT");
|
|
|
|
std::string int2_name = CLI::detail::type_name<std::int16_t>();
|
|
CHECK(int2_name == "INT");
|
|
|
|
std::string uint_name = CLI::detail::type_name<unsigned char>();
|
|
CHECK(uint_name == "UINT");
|
|
|
|
std::string float_name = CLI::detail::type_name<double>();
|
|
CHECK(float_name == "FLOAT");
|
|
|
|
std::string char_name = CLI::detail::type_name<char>();
|
|
CHECK(char_name == "CHAR");
|
|
|
|
std::string vector_name = CLI::detail::type_name<std::vector<int>>();
|
|
CHECK(vector_name == "INT");
|
|
|
|
vector_name = CLI::detail::type_name<std::vector<double>>();
|
|
CHECK(vector_name == "FLOAT");
|
|
|
|
static_assert(CLI::detail::classify_object<std::pair<int, std::string>>::value ==
|
|
CLI::detail::object_category::tuple_value,
|
|
"pair<int,string> does not read like a tuple");
|
|
|
|
static_assert(CLI::detail::classify_object<std::tuple<std::string, double>>::value ==
|
|
CLI::detail::object_category::tuple_value,
|
|
"tuple<string,double> does not read like a tuple");
|
|
|
|
std::string pair_name = CLI::detail::type_name<std::vector<std::pair<int, std::string>>>();
|
|
CHECK(pair_name == "[INT,TEXT]");
|
|
|
|
vector_name = CLI::detail::type_name<std::vector<std::vector<unsigned char>>>();
|
|
CHECK(vector_name == "UINT");
|
|
|
|
auto vclass = CLI::detail::classify_object<std::vector<std::vector<unsigned char>>>::value;
|
|
CHECK(CLI::detail::object_category::container_value == vclass);
|
|
|
|
auto tclass = CLI::detail::classify_object<std::tuple<double>>::value;
|
|
CHECK(CLI::detail::object_category::number_constructible == tclass);
|
|
|
|
std::string tuple_name = CLI::detail::type_name<std::tuple<double>>();
|
|
CHECK(tuple_name == "FLOAT");
|
|
|
|
static_assert(CLI::detail::classify_object<std::tuple<int, std::string>>::value ==
|
|
CLI::detail::object_category::tuple_value,
|
|
"tuple<int,string> does not read like a tuple");
|
|
tuple_name = CLI::detail::type_name<std::tuple<int, std::string>>();
|
|
CHECK(tuple_name == "[INT,TEXT]");
|
|
|
|
tuple_name = CLI::detail::type_name<std::tuple<const int, std::string>>();
|
|
CHECK(tuple_name == "[INT,TEXT]");
|
|
|
|
tuple_name = CLI::detail::type_name<const std::tuple<int, std::string>>();
|
|
CHECK(tuple_name == "[INT,TEXT]");
|
|
|
|
tuple_name = CLI::detail::type_name<std::tuple<std::string, double>>();
|
|
CHECK(tuple_name == "[TEXT,FLOAT]");
|
|
|
|
tuple_name = CLI::detail::type_name<const std::tuple<std::string, double>>();
|
|
CHECK(tuple_name == "[TEXT,FLOAT]");
|
|
|
|
tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double>>();
|
|
CHECK(tuple_name == "[INT,TEXT,FLOAT]");
|
|
|
|
tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double, unsigned int>>();
|
|
CHECK(tuple_name == "[INT,TEXT,FLOAT,UINT]");
|
|
|
|
tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double, unsigned int, std::string>>();
|
|
CHECK(tuple_name == "[INT,TEXT,FLOAT,UINT,TEXT]");
|
|
|
|
tuple_name = CLI::detail::type_name<std::array<int, 10>>();
|
|
CHECK(tuple_name == "[INT,INT,INT,INT,INT,INT,INT,INT,INT,INT]");
|
|
|
|
std::string text_name = CLI::detail::type_name<std::string>();
|
|
CHECK(text_name == "TEXT");
|
|
|
|
std::string text2_name = CLI::detail::type_name<char *>();
|
|
CHECK(text2_name == "TEXT");
|
|
|
|
enum class test { test1, test2, test3 };
|
|
std::string enum_name = CLI::detail::type_name<test>();
|
|
CHECK(enum_name == "ENUM");
|
|
|
|
vclass = CLI::detail::classify_object<std::tuple<test>>::value;
|
|
CHECK(CLI::detail::object_category::tuple_value == vclass);
|
|
static_assert(CLI::detail::classify_object<std::tuple<test>>::value == CLI::detail::object_category::tuple_value,
|
|
"tuple<test> does not classify as a tuple");
|
|
std::string enum_name2 = CLI::detail::type_name<std::tuple<test>>();
|
|
CHECK(enum_name2 == "ENUM");
|
|
std::string umapName = CLI::detail::type_name<std::unordered_map<int, std::tuple<std::string, double>>>();
|
|
CHECK(umapName == "[INT,[TEXT,FLOAT]]");
|
|
|
|
// On older compilers, this may show up as other/TEXT
|
|
vclass = CLI::detail::classify_object<std::atomic<int>>::value;
|
|
CHECK((CLI::detail::object_category::wrapper_value == vclass || CLI::detail::object_category::other == vclass));
|
|
|
|
std::string atomic_name = CLI::detail::type_name<std::atomic<int>>();
|
|
CHECK((atomic_name == "INT" || atomic_name == "TEXT"));
|
|
}
|
|
|
|
TEST_CASE("Types: TypeNameStrings", "[helpers]") {
|
|
auto sclass = CLI::detail::classify_object<std::string>::value;
|
|
CHECK(CLI::detail::object_category::string_assignable == sclass);
|
|
|
|
auto wsclass = CLI::detail::classify_object<std::wstring>::value;
|
|
CHECK(CLI::detail::object_category::wstring_assignable == wsclass);
|
|
|
|
#if defined CLI11_HAS_FILEYSTEM && CLI11_HAS_FILESYSTEM > 0 && defined(_MSC_VER)
|
|
auto fspclass = CLI::detail::classify_object<std::filesystem::path>::value;
|
|
CHECK(CLI::detail::object_category::wstring_assignable == fspclass);
|
|
#endif
|
|
}
|
|
|
|
TEST_CASE("Types: OverflowSmall", "[helpers]") {
|
|
signed char x = 0;
|
|
auto strmax = std::to_string((std::numeric_limits<signed char>::max)() + 1);
|
|
CHECK_FALSE(CLI::detail::lexical_cast(strmax, x));
|
|
|
|
unsigned char y = 0;
|
|
strmax = std::to_string((std::numeric_limits<unsigned char>::max)() + 1);
|
|
CHECK_FALSE(CLI::detail::lexical_cast(strmax, y));
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalCastInt", "[helpers]") {
|
|
std::string signed_input = "-912";
|
|
int x_signed = 0;
|
|
CHECK(CLI::detail::lexical_cast(signed_input, x_signed));
|
|
CHECK(x_signed == -912);
|
|
|
|
std::string unsigned_input = "912";
|
|
unsigned int x_unsigned = 0;
|
|
CHECK(CLI::detail::lexical_cast(unsigned_input, x_unsigned));
|
|
CHECK(x_unsigned == (unsigned int)912);
|
|
|
|
CHECK_FALSE(CLI::detail::lexical_cast(signed_input, x_unsigned));
|
|
|
|
unsigned char y = 0;
|
|
std::string overflow_input = std::to_string((std::numeric_limits<uint64_t>::max)()) + "0";
|
|
CHECK_FALSE(CLI::detail::lexical_cast(overflow_input, y));
|
|
|
|
char y_signed = 0;
|
|
CHECK_FALSE(CLI::detail::lexical_cast(overflow_input, y_signed));
|
|
|
|
std::string bad_input = "hello";
|
|
CHECK_FALSE(CLI::detail::lexical_cast(bad_input, y));
|
|
|
|
std::string extra_input = "912i";
|
|
CHECK_FALSE(CLI::detail::lexical_cast(extra_input, y));
|
|
|
|
extra_input = "true";
|
|
CHECK(CLI::detail::lexical_cast(extra_input, x_signed));
|
|
CHECK(x_signed != 0);
|
|
|
|
std::string empty_input{};
|
|
CHECK_FALSE(CLI::detail::lexical_cast(empty_input, x_signed));
|
|
CHECK_FALSE(CLI::detail::lexical_cast(empty_input, x_unsigned));
|
|
CHECK_FALSE(CLI::detail::lexical_cast(empty_input, y_signed));
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalCastDouble", "[helpers]") {
|
|
std::string input = "9.12";
|
|
long double x = NAN;
|
|
CHECK(CLI::detail::lexical_cast(input, x));
|
|
CHECK((float)x == Approx((float)9.12));
|
|
|
|
std::string bad_input = "hello";
|
|
CHECK_FALSE(CLI::detail::lexical_cast(bad_input, x));
|
|
|
|
std::string overflow_input = "1" + std::to_string((std::numeric_limits<long double>::max)());
|
|
CHECK(CLI::detail::lexical_cast(overflow_input, x));
|
|
CHECK_FALSE(std::isfinite(x));
|
|
|
|
std::string extra_input = "9.12i";
|
|
CHECK_FALSE(CLI::detail::lexical_cast(extra_input, x));
|
|
|
|
std::string empty_input{};
|
|
CHECK_FALSE(CLI::detail::lexical_cast(empty_input, x));
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalCastBool", "[helpers]") {
|
|
std::string input = "false";
|
|
bool x = false;
|
|
CHECK(CLI::detail::lexical_cast(input, x));
|
|
CHECK_FALSE(x);
|
|
|
|
std::string bad_input = "happy";
|
|
CHECK_FALSE(CLI::detail::lexical_cast(bad_input, x));
|
|
|
|
std::string input_true = "EnaBLE";
|
|
CHECK(CLI::detail::lexical_cast(input_true, x));
|
|
CHECK(x);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalCastString", "[helpers]") {
|
|
std::string input = "one";
|
|
std::string output;
|
|
CLI::detail::lexical_cast(input, output);
|
|
CHECK(output == input);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalCastParsable", "[helpers]") {
|
|
std::string input = "(4.2,7.3)";
|
|
std::string fail_input = "4.2,7.3";
|
|
std::string extra_input = "(4.2,7.3)e";
|
|
|
|
std::complex<double> output;
|
|
CHECK(CLI::detail::lexical_cast(input, output));
|
|
CHECK(4.2 == Approx(output.real()));
|
|
CHECK(7.3 == Approx(output.imag()));
|
|
|
|
CHECK(CLI::detail::lexical_cast("2.456", output));
|
|
CHECK(2.456 == Approx(output.real()));
|
|
CHECK(0.0 == Approx(output.imag()));
|
|
|
|
CHECK_FALSE(CLI::detail::lexical_cast(fail_input, output));
|
|
CHECK_FALSE(CLI::detail::lexical_cast(extra_input, output));
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalCastEnum", "[helpers]") {
|
|
enum t1 : signed char { v1 = 5, v3 = 7, v5 = -9 };
|
|
|
|
t1 output = v1;
|
|
CHECK(CLI::detail::lexical_cast("-9", output));
|
|
CHECK(v5 == output);
|
|
|
|
CHECK_FALSE(CLI::detail::lexical_cast("invalid", output));
|
|
enum class t2 : std::uint64_t { enum1 = 65, enum2 = 45667, enum3 = 9999999999999 };
|
|
t2 output2{t2::enum2};
|
|
CHECK(CLI::detail::lexical_cast("65", output2));
|
|
CHECK(t2::enum1 == output2);
|
|
|
|
CHECK_FALSE(CLI::detail::lexical_cast("invalid", output2));
|
|
|
|
CHECK(CLI::detail::lexical_cast("9999999999999", output2));
|
|
CHECK(t2::enum3 == output2);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionDouble", "[helpers]") {
|
|
CLI::results_t input = {"9.12"};
|
|
long double x{0.0};
|
|
bool res = CLI::detail::lexical_conversion<long double, double>(input, x);
|
|
CHECK(res);
|
|
CHECK((float)x == Approx((float)9.12));
|
|
|
|
CLI::results_t bad_input = {"hello"};
|
|
res = CLI::detail::lexical_conversion<long double, double>(bad_input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionDoubleTuple", "[helpers]") {
|
|
CLI::results_t input = {"9.12"};
|
|
std::tuple<double> x{0.0};
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK(res);
|
|
CHECK(std::get<0>(x) == Approx(9.12));
|
|
|
|
CLI::results_t bad_input = {"hello"};
|
|
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(bad_input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionVectorDouble", "[helpers]") {
|
|
CLI::results_t input = {"9.12", "10.79", "-3.54"};
|
|
std::vector<double> x;
|
|
bool res = CLI::detail::lexical_conversion<std::vector<double>, double>(input, x);
|
|
CHECK(res);
|
|
CHECK(3u == x.size());
|
|
CHECK(-3.54 == Approx(x[2]));
|
|
|
|
res = CLI::detail::lexical_conversion<std::vector<double>, std::vector<double>>(input, x);
|
|
CHECK(res);
|
|
CHECK(3u == x.size());
|
|
CHECK(-3.54 == Approx(x[2]));
|
|
}
|
|
|
|
static_assert(!CLI::detail::is_tuple_like<std::vector<double>>::value, "vector should not be like a tuple");
|
|
static_assert(CLI::detail::is_tuple_like<std::pair<double, double>>::value, "pair of double should be like a tuple");
|
|
static_assert(CLI::detail::is_tuple_like<std::array<double, 4>>::value, "std::array<double,4> should be like a tuple");
|
|
static_assert(CLI::detail::is_tuple_like<std::array<int, 10>>::value, "std::array<int,10> should be like a tuple");
|
|
static_assert(!CLI::detail::is_tuple_like<std::string>::value, "std::string should not be like a tuple");
|
|
static_assert(!CLI::detail::is_tuple_like<double>::value, "double should not be like a tuple");
|
|
static_assert(CLI::detail::is_tuple_like<std::tuple<double, int, double>>::value, "tuple should look like a tuple");
|
|
|
|
TEST_CASE("Types: LexicalConversionTuple2", "[helpers]") {
|
|
CLI::results_t input = {"9.12", "19"};
|
|
|
|
std::tuple<double, int> x{0.0, 0};
|
|
static_assert(CLI::detail::is_tuple_like<decltype(x)>::value,
|
|
"tuple type must have is_tuple_like trait to be true");
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK(res);
|
|
CHECK(19 == std::get<1>(x));
|
|
CHECK(9.12 == Approx(std::get<0>(x)));
|
|
|
|
input = {"19", "9.12"};
|
|
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionTuple3", "[helpers]") {
|
|
CLI::results_t input = {"9.12", "19", "hippo"};
|
|
std::tuple<double, int, std::string> x;
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK(res);
|
|
CHECK(19 == std::get<1>(x));
|
|
CHECK(9.12 == Approx(std::get<0>(x)));
|
|
CHECK("hippo" == std::get<2>(x));
|
|
|
|
input = {"19", "9.12"};
|
|
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionTuple4", "[helpers]") {
|
|
CLI::results_t input = {"9.12", "19", "18.6", "5.87"};
|
|
std::array<double, 4> x;
|
|
auto tsize = CLI::detail::type_count<decltype(x)>::value;
|
|
CHECK(tsize == 4);
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK(res);
|
|
CHECK(19 == Approx(std::get<1>(x)));
|
|
CHECK(9.12 == Approx(x[0]));
|
|
CHECK(18.6 == Approx(x[2]));
|
|
CHECK(5.87 == Approx(x[3]));
|
|
|
|
input = {"19", "9.12", "hippo"};
|
|
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionTuple5", "[helpers]") {
|
|
CLI::results_t input = {"9", "19", "18", "5", "235235"};
|
|
std::array<unsigned int, 5> x;
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK(res);
|
|
CHECK(19u == std::get<1>(x));
|
|
CHECK(9u == x[0]);
|
|
CHECK(18u == x[2]);
|
|
CHECK(5u == x[3]);
|
|
CHECK(235235u == x[4]);
|
|
|
|
input = {"19", "9.12", "hippo"};
|
|
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionTuple10", "[helpers]") {
|
|
CLI::results_t input = {"9", "19", "18", "5", "235235", "9", "19", "18", "5", "235235"};
|
|
std::array<unsigned int, 10> x;
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK(res);
|
|
CHECK(19u == std::get<1>(x));
|
|
CHECK(9u == x[0]);
|
|
CHECK(18u == x[2]);
|
|
CHECK(5u == x[3]);
|
|
CHECK(235235u == x[4]);
|
|
CHECK(235235u == x[9]);
|
|
input[3] = "hippo";
|
|
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionTuple10XC", "[helpers]") {
|
|
CLI::results_t input = {"9", "19", "18", "5", "235235", "9", "19", "18", "5", "235235"};
|
|
std::array<double, 10> x;
|
|
bool res = CLI::detail::lexical_conversion<decltype(x), std::array<unsigned int, 10>>(input, x);
|
|
|
|
CHECK(res);
|
|
CHECK(19.0 == std::get<1>(x));
|
|
CHECK(9.0 == x[0]);
|
|
CHECK(18.0 == x[2]);
|
|
CHECK(5.0 == x[3]);
|
|
CHECK(235235.0 == x[4]);
|
|
CHECK(235235.0 == x[9]);
|
|
input[3] = "19.7";
|
|
res = CLI::detail::lexical_conversion<decltype(x), std::array<unsigned int, 10>>(input, x);
|
|
CHECK_FALSE(res);
|
|
}
|
|
|
|
TEST_CASE("Types: LexicalConversionComplex", "[helpers]") {
|
|
CLI::results_t input = {"5.1", "3.5"};
|
|
std::complex<double> x;
|
|
bool res = CLI::detail::lexical_conversion<std::complex<double>, std::array<double, 2>>(input, x);
|
|
CHECK(res);
|
|
CHECK(5.1 == x.real());
|
|
CHECK(3.5 == x.imag());
|
|
}
|
|
|
|
static_assert(CLI::detail::is_wrapper<std::vector<double>>::value, "vector double should be a wrapper");
|
|
static_assert(CLI::detail::is_wrapper<std::vector<std::string>>::value, "vector string should be a wrapper");
|
|
static_assert(CLI::detail::is_wrapper<std::string>::value, "string should be a wrapper");
|
|
static_assert(!CLI::detail::is_wrapper<double>::value, "double should not be a wrapper");
|
|
|
|
static_assert(CLI::detail::is_mutable_container<std::vector<double>>::value, "vector class should be a container");
|
|
static_assert(CLI::detail::is_mutable_container<std::vector<std::string>>::value, "vector class should be a container");
|
|
static_assert(!CLI::detail::is_mutable_container<std::string>::value, "string should be a container");
|
|
static_assert(!CLI::detail::is_mutable_container<double>::value, "double should not be a container");
|
|
static_assert(!CLI::detail::is_mutable_container<std::array<double, 5>>::value, "array should not be a container");
|
|
|
|
static_assert(CLI::detail::is_mutable_container<std::vector<int>>::value, "vector int should be a container");
|
|
|
|
static_assert(CLI::detail::is_readable_container<std::vector<int> &>::value,
|
|
"vector int & should be a readable container");
|
|
static_assert(CLI::detail::is_readable_container<const std::vector<int>>::value,
|
|
"const vector int should be a readable container");
|
|
static_assert(CLI::detail::is_readable_container<const std::vector<int> &>::value,
|
|
"const vector int & should be a readable container");
|
|
|
|
TEST_CASE("FixNewLines: BasicCheck", "[helpers]") {
|
|
std::string input = "one\ntwo";
|
|
std::string output = "one\n; two";
|
|
std::string result = CLI::detail::fix_newlines("; ", input);
|
|
CHECK(output == result);
|
|
}
|
|
|
|
TEST_CASE("FixNewLines: EdgesCheck", "[helpers]") {
|
|
std::string input = "\none\ntwo\n";
|
|
std::string output = "\n; one\n; two\n; ";
|
|
std::string result = CLI::detail::fix_newlines("; ", input);
|
|
CHECK(output == result);
|
|
}
|
|
|
|
TEST_CASE("String: environment", "[helpers]") {
|
|
put_env("TEST1", "TESTS");
|
|
|
|
auto value = CLI::detail::get_environment_value("TEST1");
|
|
CHECK(value == "TESTS");
|
|
unset_env("TEST1");
|
|
|
|
value = CLI::detail::get_environment_value("TEST2");
|
|
CHECK(value.empty());
|
|
}
|