mirror of
https://github.com/CLIUtils/CLI11.git
synced 2026-01-19 04:52:08 +00:00
More fuzzing2 (#1170)
add mechanic for fuzzing subcommands, and several fixes for found issues from longer fuzzer runs This includes some issues with option group positional name ambiguity, issue with join multioption policy and config files, and a few edge cases for configuration of multiline output interpretation. Also added complex variables to the options, no issues found from this addition. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
305
fuzz/fuzzApp.cpp
305
fuzz/fuzzApp.cpp
@@ -68,6 +68,9 @@ std::shared_ptr<CLI::App> FuzzApp::generateApp() {
|
|||||||
fApp->add_option("--dopt1", v1);
|
fApp->add_option("--dopt1", v1);
|
||||||
fApp->add_option("--dopt2", v2);
|
fApp->add_option("--dopt2", v2);
|
||||||
|
|
||||||
|
fApp->add_option("--cv3", cv3);
|
||||||
|
fApp->add_option("--cv4", cv4);
|
||||||
|
|
||||||
auto *vgroup = fApp->add_option_group("vectors");
|
auto *vgroup = fApp->add_option_group("vectors");
|
||||||
|
|
||||||
vgroup->add_option("--vopt1", vv1);
|
vgroup->add_option("--vopt1", vv1);
|
||||||
@@ -119,7 +122,7 @@ std::shared_ptr<CLI::App> FuzzApp::generateApp() {
|
|||||||
|
|
||||||
sub->add_option("--stup1", p1);
|
sub->add_option("--stup1", p1);
|
||||||
sub->add_option("--stup2", t1);
|
sub->add_option("--stup2", t1);
|
||||||
sub->add_option("--stup4", tcomplex);
|
sub->add_option("--stup4", tcomplex2);
|
||||||
sub->add_option("--svtup", vectup);
|
sub->add_option("--svtup", vectup);
|
||||||
|
|
||||||
sub->add_option("--sdwrap", dwrap);
|
sub->add_option("--sdwrap", dwrap);
|
||||||
@@ -172,6 +175,8 @@ static void print_string_comparison(const std::string &s1,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr double keydub = 6262542.2622;
|
||||||
bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
||||||
if(val32 != other.val32) {
|
if(val32 != other.val32) {
|
||||||
return false;
|
return false;
|
||||||
@@ -207,10 +212,14 @@ bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(v1 != other.v1) {
|
if(v1 != other.v1) {
|
||||||
return false;
|
if(!(std::isnan(v1) && std::isnan(other.v1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(v2 != other.v2) {
|
if(v2 != other.v2) {
|
||||||
return false;
|
if(!(std::isnan(v2) && std::isnan(other.v2))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vv1 != other.vv1) {
|
if(vv1 != other.vv1) {
|
||||||
@@ -232,13 +241,34 @@ bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(vecvecd != other.vecvecd) {
|
if(vecvecd != other.vecvecd) {
|
||||||
return false;
|
if(vecvecd.size() != other.vecvecd.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// need to check if they are both nan
|
||||||
|
for(std::size_t index = 0; index < vecvecd.size(); ++index) {
|
||||||
|
if(vecvecd[index].size() != other.vecvecd[index].size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(vecvecd[index] != other.vecvecd[index]) {
|
||||||
|
for(std::size_t jj = 0; jj < vecvecd[index].size(); ++jj) {
|
||||||
|
if(std::isnan(vecvecd[index][jj]) && std::isnan(other.vecvecd[index][jj])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(vvs != other.vvs) {
|
if(vvs != other.vvs) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(od1 != other.od1) {
|
if(od1 != other.od1) {
|
||||||
return false;
|
if(!od1 || !other.od1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!(std::isnan(*od1) && std::isnan(*other.od1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(ods != other.ods) {
|
if(ods != other.ods) {
|
||||||
return false;
|
return false;
|
||||||
@@ -247,22 +277,160 @@ bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(p1 != other.p1) {
|
if(p1 != other.p1) {
|
||||||
return false;
|
if(p1.second != other.p1.second) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!(std::isnan(p1.first) && std::isnan(other.p1.first))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(p2 != other.p2) {
|
if(p2 != other.p2) {
|
||||||
return false;
|
if(p2.second != other.p2.second) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(p2.first.size() != other.p2.first.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(std::size_t index = 0; index < p2.first.size(); ++index) {
|
||||||
|
if(p2.first[index] != other.p2.first[index]) {
|
||||||
|
if(std::isnan(p2.first[index]) && std::isnan(other.p2.first[index])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(t1 != other.t1) {
|
if(t1 != other.t1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if(cv3 != other.cv3) {
|
||||||
|
if(cv3.real() != other.cv3.real()) {
|
||||||
|
if(!(std::isnan(cv3.real()) && std::isnan(other.cv3.real()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cv3.imag() != other.cv3.imag()) {
|
||||||
|
if(!(std::isnan(cv3.imag()) && std::isnan(other.cv3.imag()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cv4 != other.cv4) {
|
||||||
|
if(cv4.real() != other.cv4.real()) {
|
||||||
|
if(!(std::isnan(cv4.real()) && std::isnan(other.cv4.real()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cv4.imag() != other.cv4.imag()) {
|
||||||
|
if(!(std::isnan(cv4.imag()) && std::isnan(other.cv4.imag()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(tcomplex != other.tcomplex) {
|
if(tcomplex != other.tcomplex) {
|
||||||
return false;
|
|
||||||
|
if(std::get<0>(tcomplex) != std::get<0>(other.tcomplex)) {
|
||||||
|
auto testa = std::get<0>(tcomplex);
|
||||||
|
auto testb = std::get<0>(other.tcomplex);
|
||||||
|
if(std::isnan(std::get<double>(testa))) {
|
||||||
|
std::get<double>(testa) = keydub;
|
||||||
|
}
|
||||||
|
if(std::isnan(std::get<double>(testb))) {
|
||||||
|
std::get<double>(testb) = keydub;
|
||||||
|
}
|
||||||
|
if(std::isnan(std::get<double>(std::get<0>(testa)))) {
|
||||||
|
std::get<double>(std::get<0>(testa)) = keydub;
|
||||||
|
}
|
||||||
|
if(std::isnan(std::get<double>(std::get<0>(testb)))) {
|
||||||
|
std::get<double>(std::get<0>(testb)) = keydub;
|
||||||
|
}
|
||||||
|
if(testa != testb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(tcomplex2 != other.tcomplex2) {
|
if(tcomplex2 != other.tcomplex2) {
|
||||||
return false;
|
if(std::get<0>(tcomplex2) != std::get<0>(other.tcomplex2)) {
|
||||||
|
auto testa = std::get<0>(tcomplex2);
|
||||||
|
auto testb = std::get<0>(other.tcomplex2);
|
||||||
|
if(std::isnan(std::get<double>(testa))) {
|
||||||
|
std::get<double>(testa) = keydub;
|
||||||
|
}
|
||||||
|
if(std::isnan(std::get<double>(testb))) {
|
||||||
|
std::get<double>(testb) = keydub;
|
||||||
|
}
|
||||||
|
if(std::isnan(std::get<double>(std::get<0>(testa)))) {
|
||||||
|
std::get<double>(std::get<0>(testa)) = keydub;
|
||||||
|
}
|
||||||
|
if(std::isnan(std::get<double>(std::get<0>(testb)))) {
|
||||||
|
std::get<double>(std::get<0>(testb)) = keydub;
|
||||||
|
}
|
||||||
|
if(testa != testb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(vectup != other.vectup) {
|
if(vectup != other.vectup) {
|
||||||
return false;
|
bool found_diff = false;
|
||||||
|
if(vectup.size() != other.vectup.size()) {
|
||||||
|
if(print_error) {
|
||||||
|
std::cout << "size is different vectup.size()=" << vectup.size()
|
||||||
|
<< " other.vectup.size=" << other.vectup.size() << '\n';
|
||||||
|
}
|
||||||
|
found_diff = true;
|
||||||
|
} else {
|
||||||
|
for(size_t ii = 0; ii < vectup.size(); ++ii) {
|
||||||
|
if(vectup[ii] != other.vectup[ii]) {
|
||||||
|
int matching = 4;
|
||||||
|
if(std::get<0>(vectup[ii]) != std::get<0>(other.vectup[ii])) {
|
||||||
|
--matching;
|
||||||
|
if(print_error) {
|
||||||
|
std::cout << "vectup[" << ii << "][0] != other.vectup[" << ii << "][0]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(std::get<1>(vectup[ii]) != std::get<1>(other.vectup[ii])) {
|
||||||
|
if(!(std::isnan(std::get<1>(vectup[ii])) && std::isnan(std::get<1>(other.vectup[ii])))) {
|
||||||
|
--matching;
|
||||||
|
if(print_error) {
|
||||||
|
std::cout << "vectup[" << ii << "][1] != other.vectup[" << ii << "][1]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(std::get<2>(vectup[ii]) != std::get<2>(other.vectup[ii])) {
|
||||||
|
--matching;
|
||||||
|
if(print_error) {
|
||||||
|
std::cout << "vectup[" << ii << "][2] != other.vectup[" << ii << "][2]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(std::get<3>(vectup[ii]) != std::get<3>(other.vectup[ii])) {
|
||||||
|
--matching;
|
||||||
|
if(print_error) {
|
||||||
|
std::cout << "vectup[" << ii << "][3] != other.vectup[" << ii << "][3]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(matching != 4) {
|
||||||
|
found_diff = true;
|
||||||
|
if(print_error) {
|
||||||
|
std::cout << "vectup[" << ii << "] != other.vectup[" << ii << "]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*print_string_comparison(vstrA[ii],
|
||||||
|
other.vectup[ii],
|
||||||
|
std::string("string[") + std::to_string(ii) + ']',
|
||||||
|
"vstrA",
|
||||||
|
"other.vstrA");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(found_diff) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(vstrv != other.vstrv) {
|
if(vstrv != other.vstrv) {
|
||||||
return false;
|
return false;
|
||||||
@@ -282,7 +450,9 @@ bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(dwrap.value() != other.dwrap.value()) {
|
if(dwrap.value() != other.dwrap.value()) {
|
||||||
return false;
|
if(!(std::isnan(dwrap.value()) && std::isnan(other.dwrap.value()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(swrap.value() != other.swrap.value()) {
|
if(swrap.value() != other.swrap.value()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -294,11 +464,27 @@ bool FuzzApp::compare(const FuzzApp &other, bool print_error) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(doubleAtomic != other.doubleAtomic) {
|
if(doubleAtomic != other.doubleAtomic) {
|
||||||
return false;
|
if(!(std::isnan(doubleAtomic.load()) && std::isnan(other.doubleAtomic.load()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for testing restrictions and reduction methods
|
// for testing restrictions and reduction methods
|
||||||
if(vstrA != other.vstrA) {
|
if(vstrA != other.vstrA) {
|
||||||
|
if(print_error) {
|
||||||
|
if(vstrA.size() != other.vstrA.size()) {
|
||||||
|
std::cout << "size is different vstrA.size()=" << vstrA.size()
|
||||||
|
<< " other.vstrA.size=" << other.vstrA.size() << '\n';
|
||||||
|
} else {
|
||||||
|
for(size_t ii = 0; ii < vstrA.size(); ++ii) {
|
||||||
|
print_string_comparison(vstrA[ii],
|
||||||
|
other.vstrA[ii],
|
||||||
|
std::string("string[") + std::to_string(ii) + ']',
|
||||||
|
"vstrA",
|
||||||
|
"other.vstrA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(vstrB != other.vstrB) {
|
if(vstrB != other.vstrB) {
|
||||||
@@ -423,7 +609,6 @@ void FuzzApp::modify_option(CLI::Option *opt, const std::string &modifier_string
|
|||||||
case '|':
|
case '|':
|
||||||
case '\\':
|
case '\\':
|
||||||
case '~':
|
case '~':
|
||||||
case ':':
|
|
||||||
opt->delimiter(mod);
|
opt->delimiter(mod);
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
@@ -476,6 +661,89 @@ void FuzzApp::modify_option(CLI::Option *opt, const std::string &modifier_string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FuzzApp::modify_subcommand(CLI::App *app, const std::string &modifiers) {
|
||||||
|
for(const auto mod : modifiers) {
|
||||||
|
switch(mod) {
|
||||||
|
case 'w':
|
||||||
|
case 'W':
|
||||||
|
app->allow_windows_style_options(mod < '`');
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
case 'N':
|
||||||
|
app->allow_non_standard_option_names(mod < '`');
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
case 'P':
|
||||||
|
app->allow_subcommand_prefix_matching(mod < '`');
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
app->fallthrough(mod < '`');
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
case 'V':
|
||||||
|
app->validate_positionals(mod < '`');
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
app->positionals_at_end(mod < '`');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubcommandData extract_subcomand_info(const std::string &description_string, std::size_t index) {
|
||||||
|
SubcommandData sub_data;
|
||||||
|
sub_data.next = index;
|
||||||
|
int depth = 1;
|
||||||
|
// end of prefix section for <subcommand
|
||||||
|
auto first_sub_label = description_string.find_first_of('>', index + 12);
|
||||||
|
if(first_sub_label == std::string::npos) {
|
||||||
|
return sub_data;
|
||||||
|
}
|
||||||
|
auto end_sub_label = first_sub_label;
|
||||||
|
auto end_sub = description_string.find("</subcommand>", end_sub_label + 1);
|
||||||
|
auto start_sub = description_string.find("<subcommand", end_sub_label + 1);
|
||||||
|
while(depth > 0) {
|
||||||
|
if(end_sub == std::string::npos) {
|
||||||
|
return sub_data;
|
||||||
|
}
|
||||||
|
depth += (end_sub < start_sub) ? -1 : 1;
|
||||||
|
|
||||||
|
if(depth > 0) {
|
||||||
|
if(start_sub != std::string::npos) {
|
||||||
|
end_sub_label = description_string.find_first_of('>', start_sub + 12);
|
||||||
|
if(end_sub_label == std::string::npos) {
|
||||||
|
return sub_data;
|
||||||
|
}
|
||||||
|
end_sub = description_string.find("</subcommand>", end_sub_label + 1);
|
||||||
|
start_sub = description_string.find("<subcommand", end_sub_label + 1);
|
||||||
|
} else {
|
||||||
|
end_sub = description_string.find("</subcommand>", end_sub + 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub_data.data = description_string.substr(first_sub_label + 1, end_sub - first_sub_label - 1);
|
||||||
|
std::string metadata = description_string.substr(index + 12, end_sub_label - index - 12);
|
||||||
|
auto fields = detail::split_up(metadata);
|
||||||
|
for(auto &field : fields) {
|
||||||
|
if(field.compare(0, 5, "name=") == 0) {
|
||||||
|
sub_data.name = field.substr(5);
|
||||||
|
detail::process_quoted_string(sub_data.name);
|
||||||
|
} else if(field.compare(0, 11, "description=") == 0) {
|
||||||
|
sub_data.description = field.substr(11);
|
||||||
|
detail::process_quoted_string(sub_data.description);
|
||||||
|
} else if(field.compare(0, 10, "modifiers=") == 0) {
|
||||||
|
sub_data.modifiers = field.substr(10);
|
||||||
|
detail::process_quoted_string(sub_data.modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub_data.next = end_sub + 13;
|
||||||
|
return sub_data;
|
||||||
|
}
|
||||||
|
|
||||||
//<option>name_string</option>
|
//<option>name_string</option>
|
||||||
//<vector>name_string</vector>
|
//<vector>name_string</vector>
|
||||||
//<flag>name_string</flag>
|
//<flag>name_string</flag>
|
||||||
@@ -564,7 +832,16 @@ std::size_t FuzzApp::add_custom_options(CLI::App *app, const std::string &descri
|
|||||||
if(end_sub == std::string::npos) {
|
if(end_sub == std::string::npos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
auto subdata = extract_subcomand_info(description_string, current_index);
|
||||||
|
if(subdata.data.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto *sub = app->add_subcommand(subdata.name, subdata.description);
|
||||||
|
if(!subdata.modifiers.empty()) {
|
||||||
|
modify_subcommand(sub, subdata.modifiers);
|
||||||
|
}
|
||||||
|
add_custom_options(sub, subdata.data);
|
||||||
|
current_index = subdata.next;
|
||||||
} else {
|
} else {
|
||||||
if(isspace(description_string[current_index]) != 0) {
|
if(isspace(description_string[current_index]) != 0) {
|
||||||
++current_index;
|
++current_index;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <complex>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -51,6 +52,18 @@ class stringWrapper {
|
|||||||
std::string val{};
|
std::string val{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief class for extracting data for custom subcommand generation.
|
||||||
|
class SubcommandData {
|
||||||
|
public:
|
||||||
|
std::string name{""};
|
||||||
|
std::string description{""};
|
||||||
|
std::string modifiers{""};
|
||||||
|
std::string data{""};
|
||||||
|
std::size_t next{std::string::npos};
|
||||||
|
};
|
||||||
|
|
||||||
|
SubcommandData extract_subcomand_info(const std::string &description_string, std::size_t index);
|
||||||
|
|
||||||
class FuzzApp {
|
class FuzzApp {
|
||||||
public:
|
public:
|
||||||
FuzzApp() = default;
|
FuzzApp() = default;
|
||||||
@@ -62,7 +75,9 @@ class FuzzApp {
|
|||||||
std::size_t add_custom_options(CLI::App *app, const std::string &description_string);
|
std::size_t add_custom_options(CLI::App *app, const std::string &description_string);
|
||||||
/** modify an option based on string*/
|
/** modify an option based on string*/
|
||||||
static void modify_option(CLI::Option *opt, const std::string &modifier);
|
static void modify_option(CLI::Option *opt, const std::string &modifier);
|
||||||
|
/** modify a subcommand based on characters in a string*/
|
||||||
|
static void modify_subcommand(CLI::App *app, const std::string &modifiers);
|
||||||
|
/** return true if the app itself support conversiont to config files*/
|
||||||
CLI11_NODISCARD bool supports_config_file() const { return !non_config_required; }
|
CLI11_NODISCARD bool supports_config_file() const { return !non_config_required; }
|
||||||
int32_t val32{0};
|
int32_t val32{0};
|
||||||
int16_t val16{0};
|
int16_t val16{0};
|
||||||
@@ -110,6 +125,9 @@ class FuzzApp {
|
|||||||
doubleWrapper dwrap{0.0};
|
doubleWrapper dwrap{0.0};
|
||||||
stringWrapper swrap{};
|
stringWrapper swrap{};
|
||||||
std::string buffer{};
|
std::string buffer{};
|
||||||
|
std::complex<double> cv3{0.0, 0.0};
|
||||||
|
std::complex<float> cv4{0.0, 0.0};
|
||||||
|
|
||||||
int intbuffer{0};
|
int intbuffer{0};
|
||||||
std::atomic<double> doubleAtomic{0.0};
|
std::atomic<double> doubleAtomic{0.0};
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
"--tup1"
|
"--tup1"
|
||||||
"--tup2"
|
"--tup2"
|
||||||
"--tup4"
|
"--tup4"
|
||||||
|
"--cv3"
|
||||||
|
"--cv4"
|
||||||
"--dwrap"
|
"--dwrap"
|
||||||
"--iwrap"
|
"--iwrap"
|
||||||
"--vtup"
|
"--vtup"
|
||||||
@@ -118,6 +120,8 @@
|
|||||||
"tup1"
|
"tup1"
|
||||||
"tup2"
|
"tup2"
|
||||||
"tup4"
|
"tup4"
|
||||||
|
"cv3"
|
||||||
|
"cv4"
|
||||||
"dwrap"
|
"dwrap"
|
||||||
"iwrap"
|
"iwrap"
|
||||||
"swrap"
|
"swrap"
|
||||||
@@ -181,6 +185,12 @@
|
|||||||
"<flag "
|
"<flag "
|
||||||
"<flag>"
|
"<flag>"
|
||||||
"</flag>"
|
"</flag>"
|
||||||
|
"<subcommand"
|
||||||
|
"<subcommand>"
|
||||||
|
"</subcommand>"
|
||||||
">-"
|
">-"
|
||||||
">--"
|
">--"
|
||||||
"modifiers="
|
"modifiers="
|
||||||
|
"name="
|
||||||
|
"description="
|
||||||
|
"alias="
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ using callback_t = std::function<bool(const results_t &)>;
|
|||||||
|
|
||||||
class Option;
|
class Option;
|
||||||
class App;
|
class App;
|
||||||
|
class ConfigBase;
|
||||||
|
|
||||||
using Option_p = std::unique_ptr<Option>;
|
using Option_p = std::unique_ptr<Option>;
|
||||||
/// Enumeration of the multiOption Policy selection
|
/// Enumeration of the multiOption Policy selection
|
||||||
@@ -51,6 +52,7 @@ enum class MultiOptionPolicy : char {
|
|||||||
/// to share parts of the class; an OptionDefaults can copy to an Option.
|
/// to share parts of the class; an OptionDefaults can copy to an Option.
|
||||||
template <typename CRTP> class OptionBase {
|
template <typename CRTP> class OptionBase {
|
||||||
friend App;
|
friend App;
|
||||||
|
friend ConfigBase;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// The group membership
|
/// The group membership
|
||||||
@@ -230,6 +232,7 @@ class OptionDefaults : public OptionBase<OptionDefaults> {
|
|||||||
|
|
||||||
class Option : public OptionBase<Option> {
|
class Option : public OptionBase<Option> {
|
||||||
friend App;
|
friend App;
|
||||||
|
friend ConfigBase;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// @name Names
|
/// @name Names
|
||||||
|
|||||||
@@ -263,7 +263,10 @@ CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string);
|
|||||||
CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string);
|
CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string);
|
||||||
|
|
||||||
/// process a quoted string, remove the quotes and if appropriate handle escaped characters
|
/// process a quoted string, remove the quotes and if appropriate handle escaped characters
|
||||||
CLI11_INLINE bool process_quoted_string(std::string &str, char string_char = '\"', char literal_char = '\'');
|
CLI11_INLINE bool process_quoted_string(std::string &str,
|
||||||
|
char string_char = '\"',
|
||||||
|
char literal_char = '\'',
|
||||||
|
bool disable_secondary_array_processing = false);
|
||||||
|
|
||||||
/// This function formats the given text as a paragraph with fixed width and applies correct line wrapping
|
/// This function formats the given text as a paragraph with fixed width and applies correct line wrapping
|
||||||
/// with a custom line prefix. The paragraph will get streamed to the given ostream.
|
/// with a custom line prefix. The paragraph will get streamed to the given ostream.
|
||||||
|
|||||||
@@ -165,94 +165,103 @@ CLI11_INLINE Option *App::add_option(std::string option_name,
|
|||||||
std::function<std::string()> func) {
|
std::function<std::string()> func) {
|
||||||
Option myopt{option_name, option_description, option_callback, this, allow_non_standard_options_};
|
Option myopt{option_name, option_description, option_callback, this, allow_non_standard_options_};
|
||||||
|
|
||||||
if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { return *v == myopt; }) ==
|
// do a quick search in current subcommand for options
|
||||||
std::end(options_)) {
|
auto res =
|
||||||
if(myopt.lnames_.empty() && myopt.snames_.empty()) {
|
std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { return *v == myopt; });
|
||||||
// if the option is positional only there is additional potential for ambiguities in config files and needs
|
if(res != options_.end()) {
|
||||||
// to be checked
|
const auto &matchname = (*res)->matching_name(myopt);
|
||||||
std::string test_name = "--" + myopt.get_single_name();
|
throw(OptionAlreadyAdded("added option matched existing option name: " + matchname));
|
||||||
if(test_name.size() == 3) {
|
}
|
||||||
test_name.erase(0, 1);
|
/** get a top level parent*/
|
||||||
}
|
const App *top_level_parent = this;
|
||||||
|
while(top_level_parent->name_.empty() && top_level_parent->parent_ != nullptr) {
|
||||||
|
top_level_parent = top_level_parent->parent_;
|
||||||
|
}
|
||||||
|
|
||||||
auto *op = get_option_no_throw(test_name);
|
if(myopt.lnames_.empty() && myopt.snames_.empty()) {
|
||||||
|
// if the option is positional only there is additional potential for ambiguities in config files and needs
|
||||||
|
// to be checked
|
||||||
|
std::string test_name = "--" + myopt.get_single_name();
|
||||||
|
if(test_name.size() == 3) {
|
||||||
|
test_name.erase(0, 1);
|
||||||
|
}
|
||||||
|
// if we are in option group
|
||||||
|
const auto *op = top_level_parent->get_option_no_throw(test_name);
|
||||||
|
if(op != nullptr && op->get_configurable()) {
|
||||||
|
throw(OptionAlreadyAdded("added option positional name matches existing option: " + test_name));
|
||||||
|
}
|
||||||
|
// need to check if there is another positional with the same name that also doesn't have any long or
|
||||||
|
// short names
|
||||||
|
op = top_level_parent->get_option_no_throw(myopt.get_single_name());
|
||||||
|
if(op != nullptr && op->lnames_.empty() && op->snames_.empty()) {
|
||||||
|
throw(OptionAlreadyAdded("unable to disambiguate with existing option: " + test_name));
|
||||||
|
}
|
||||||
|
} else if(top_level_parent != this) {
|
||||||
|
for(auto &ln : myopt.lnames_) {
|
||||||
|
const auto *op = top_level_parent->get_option_no_throw(ln);
|
||||||
if(op != nullptr && op->get_configurable()) {
|
if(op != nullptr && op->get_configurable()) {
|
||||||
throw(OptionAlreadyAdded("added option positional name matches existing option: " + test_name));
|
throw(OptionAlreadyAdded("added option matches existing positional option: " + ln));
|
||||||
}
|
}
|
||||||
// need to check if there is another positional with the same name that also doesn't have any long or short
|
op = top_level_parent->get_option_no_throw("--" + ln);
|
||||||
// names
|
if(op != nullptr && op->get_configurable()) {
|
||||||
op = get_option_no_throw(myopt.get_single_name());
|
throw(OptionAlreadyAdded("added option matches existing option: --" + ln));
|
||||||
if(op != nullptr && op->lnames_.empty() && op->snames_.empty()) {
|
|
||||||
throw(OptionAlreadyAdded("unable to disambiguate with existing option: " + test_name));
|
|
||||||
}
|
}
|
||||||
} else if(parent_ != nullptr) {
|
}
|
||||||
for(auto &ln : myopt.lnames_) {
|
for(auto &sn : myopt.snames_) {
|
||||||
auto *op = parent_->get_option_no_throw(ln);
|
const auto *op = top_level_parent->get_option_no_throw(sn);
|
||||||
if(op != nullptr && op->get_configurable()) {
|
if(op != nullptr && op->get_configurable()) {
|
||||||
throw(OptionAlreadyAdded("added option matches existing positional option: " + ln));
|
throw(OptionAlreadyAdded("added option matches existing positional option: " + sn));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for(auto &sn : myopt.snames_) {
|
op = top_level_parent->get_option_no_throw("-" + sn);
|
||||||
auto *op = parent_->get_option_no_throw(sn);
|
if(op != nullptr && op->get_configurable()) {
|
||||||
if(op != nullptr && op->get_configurable()) {
|
throw(OptionAlreadyAdded("added option matches existing option: -" + sn));
|
||||||
throw(OptionAlreadyAdded("added option matches existing positional option: " + sn));
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(allow_non_standard_options_ && !myopt.snames_.empty()) {
|
||||||
|
|
||||||
|
for(auto &sname : myopt.snames_) {
|
||||||
|
if(sname.length() > 1) {
|
||||||
|
std::string test_name;
|
||||||
|
test_name.push_back('-');
|
||||||
|
test_name.push_back(sname.front());
|
||||||
|
const auto *op = top_level_parent->get_option_no_throw(test_name);
|
||||||
|
if(op != nullptr) {
|
||||||
|
throw(OptionAlreadyAdded("added option interferes with existing short option: " + sname));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(allow_non_standard_options_ && !myopt.snames_.empty()) {
|
for(auto &opt : top_level_parent->get_options()) {
|
||||||
for(auto &sname : myopt.snames_) {
|
for(const auto &osn : opt->snames_) {
|
||||||
if(sname.length() > 1) {
|
if(osn.size() > 1) {
|
||||||
std::string test_name;
|
std::string test_name;
|
||||||
test_name.push_back('-');
|
test_name.push_back(osn.front());
|
||||||
test_name.push_back(sname.front());
|
if(myopt.check_sname(test_name)) {
|
||||||
auto *op = get_option_no_throw(test_name);
|
throw(OptionAlreadyAdded("added option interferes with existing non standard option: " + osn));
|
||||||
if(op != nullptr) {
|
|
||||||
throw(OptionAlreadyAdded("added option interferes with existing short option: " + sname));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(auto &opt : options_) {
|
|
||||||
for(const auto &osn : opt->snames_) {
|
|
||||||
if(osn.size() > 1) {
|
|
||||||
std::string test_name;
|
|
||||||
test_name.push_back(osn.front());
|
|
||||||
if(myopt.check_sname(test_name)) {
|
|
||||||
throw(OptionAlreadyAdded("added option interferes with existing non standard option: " +
|
|
||||||
osn));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options_.emplace_back();
|
|
||||||
Option_p &option = options_.back();
|
|
||||||
option.reset(new Option(option_name, option_description, option_callback, this, allow_non_standard_options_));
|
|
||||||
|
|
||||||
// Set the default string capture function
|
|
||||||
option->default_function(func);
|
|
||||||
|
|
||||||
// For compatibility with CLI11 1.7 and before, capture the default string here
|
|
||||||
if(defaulted)
|
|
||||||
option->capture_default_str();
|
|
||||||
|
|
||||||
// Transfer defaults to the new option
|
|
||||||
option_defaults_.copy_to(option.get());
|
|
||||||
|
|
||||||
// Don't bother to capture if we already did
|
|
||||||
if(!defaulted && option->get_always_capture_default())
|
|
||||||
option->capture_default_str();
|
|
||||||
|
|
||||||
return option.get();
|
|
||||||
}
|
}
|
||||||
// we know something matches now find what it is so we can produce more error information
|
options_.emplace_back();
|
||||||
for(auto &opt : options_) {
|
Option_p &option = options_.back();
|
||||||
const auto &matchname = opt->matching_name(myopt);
|
option.reset(new Option(option_name, option_description, option_callback, this, allow_non_standard_options_));
|
||||||
if(!matchname.empty()) {
|
|
||||||
throw(OptionAlreadyAdded("added option matched existing option name: " + matchname));
|
// Set the default string capture function
|
||||||
}
|
option->default_function(func);
|
||||||
}
|
|
||||||
// this line should not be reached the above loop should trigger the throw
|
// For compatibility with CLI11 1.7 and before, capture the default string here
|
||||||
throw(OptionAlreadyAdded("added option matched existing option name")); // LCOV_EXCL_LINE
|
if(defaulted)
|
||||||
|
option->capture_default_str();
|
||||||
|
|
||||||
|
// Transfer defaults to the new option
|
||||||
|
option_defaults_.copy_to(option.get());
|
||||||
|
|
||||||
|
// Don't bother to capture if we already did
|
||||||
|
if(!defaulted && option->get_always_capture_default())
|
||||||
|
option->capture_default_str();
|
||||||
|
|
||||||
|
return option.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string &help_description) {
|
CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string &help_description) {
|
||||||
@@ -841,8 +850,8 @@ CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Opt
|
|||||||
std::end(options));
|
std::end(options));
|
||||||
}
|
}
|
||||||
for(auto &subc : subcommands_) {
|
for(auto &subc : subcommands_) {
|
||||||
// also check down into nameless subcommands
|
// also check down into nameless subcommands and specific groups
|
||||||
if(subc->get_name().empty() && !subc->get_group().empty() && subc->get_group().front() == '+') {
|
if(subc->get_name().empty() || (!subc->get_group().empty() && subc->get_group().front() == '+')) {
|
||||||
auto subcopts = subc->get_options(filter);
|
auto subcopts = subc->get_options(filter);
|
||||||
options.insert(options.end(), subcopts.begin(), subcopts.end());
|
options.insert(options.end(), subcopts.begin(), subcopts.end());
|
||||||
}
|
}
|
||||||
@@ -1539,7 +1548,8 @@ App::_add_flag_like_result(Option *op, const ConfigItem &item, const std::vector
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(static_cast<int>(inputs.size()) > op->get_items_expected_max() &&
|
if(static_cast<int>(inputs.size()) > op->get_items_expected_max() &&
|
||||||
op->get_multi_option_policy() != MultiOptionPolicy::TakeAll) {
|
op->get_multi_option_policy() != MultiOptionPolicy::TakeAll &&
|
||||||
|
op->get_multi_option_policy() != MultiOptionPolicy::Join) {
|
||||||
if(op->get_items_expected_max() > 1) {
|
if(op->get_items_expected_max() > 1) {
|
||||||
throw ArgumentMismatch::AtMost(item.fullname(), op->get_items_expected_max(), inputs.size());
|
throw ArgumentMismatch::AtMost(item.fullname(), op->get_items_expected_max(), inputs.size());
|
||||||
}
|
}
|
||||||
@@ -1608,11 +1618,6 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
|
|||||||
}
|
}
|
||||||
if(op == nullptr) {
|
if(op == nullptr) {
|
||||||
op = get_option_no_throw(item.name);
|
op = get_option_no_throw(item.name);
|
||||||
} else if(!op->get_configurable()) {
|
|
||||||
auto *testop = get_option_no_throw(item.name);
|
|
||||||
if(testop != nullptr && testop->get_configurable()) {
|
|
||||||
op = testop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if(!op->get_configurable()) {
|
} else if(!op->get_configurable()) {
|
||||||
if(item.name.size() == 1) {
|
if(item.name.size() == 1) {
|
||||||
@@ -1621,14 +1626,17 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
|
|||||||
op = testop;
|
op = testop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!op->get_configurable()) {
|
}
|
||||||
auto *testop = get_option_no_throw(item.name);
|
if(op == nullptr || !op->get_configurable()) {
|
||||||
if(testop != nullptr && testop->get_configurable()) {
|
std::string iname = item.name;
|
||||||
op = testop;
|
auto options = get_options([iname](const CLI::Option *opt) {
|
||||||
}
|
return (opt->get_configurable() &&
|
||||||
|
(opt->check_name(iname) || opt->check_lname(iname) || opt->check_sname(iname)));
|
||||||
|
});
|
||||||
|
if(!options.empty()) {
|
||||||
|
op = options[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(op == nullptr) {
|
if(op == nullptr) {
|
||||||
// If the option was not present
|
// If the option was not present
|
||||||
if(get_allow_config_extras() == config_extras_mode::capture) {
|
if(get_allow_config_extras() == config_extras_mode::capture) {
|
||||||
@@ -1785,7 +1793,9 @@ CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool ha
|
|||||||
posOpt->add_result(std::string{});
|
posOpt->add_result(std::string{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
results_t prev;
|
||||||
if(posOpt->get_trigger_on_parse() && posOpt->current_option_state_ == Option::option_state::callback_run) {
|
if(posOpt->get_trigger_on_parse() && posOpt->current_option_state_ == Option::option_state::callback_run) {
|
||||||
|
prev = posOpt->results();
|
||||||
posOpt->clear();
|
posOpt->clear();
|
||||||
}
|
}
|
||||||
if(posOpt->get_expected_min() == 0) {
|
if(posOpt->get_expected_min() == 0) {
|
||||||
@@ -1801,6 +1811,10 @@ CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool ha
|
|||||||
if(posOpt->get_trigger_on_parse()) {
|
if(posOpt->get_trigger_on_parse()) {
|
||||||
if(!posOpt->empty()) {
|
if(!posOpt->empty()) {
|
||||||
posOpt->run_callback();
|
posOpt->run_callback();
|
||||||
|
} else {
|
||||||
|
if(!prev.empty()) {
|
||||||
|
posOpt->add_result(prev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ convert_arg_for_ini(const std::string &arg, char stringQuote, char literalQuote,
|
|||||||
return_string.push_back('\n');
|
return_string.push_back('\n');
|
||||||
}
|
}
|
||||||
return_string.append(arg);
|
return_string.append(arg);
|
||||||
|
if(arg.back() == '\n' || arg.back() == '\r') {
|
||||||
|
return_string.push_back('\n');
|
||||||
|
}
|
||||||
return_string.append(multiline_literal_quote, 3);
|
return_string.append(multiline_literal_quote, 3);
|
||||||
return return_string;
|
return return_string;
|
||||||
}
|
}
|
||||||
@@ -349,7 +352,7 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
|
|||||||
inMLineValue = true;
|
inMLineValue = true;
|
||||||
bool lineExtension{false};
|
bool lineExtension{false};
|
||||||
bool firstLine = true;
|
bool firstLine = true;
|
||||||
if(!item.empty() && item.back() == '\\') {
|
if(!item.empty() && item.back() == '\\' && keyChar == '\"') {
|
||||||
item.pop_back();
|
item.pop_back();
|
||||||
lineExtension = true;
|
lineExtension = true;
|
||||||
} else if(detail::hasMLString(item, keyChar)) {
|
} else if(detail::hasMLString(item, keyChar)) {
|
||||||
@@ -435,7 +438,7 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
|
|||||||
std::vector<std::string> parents;
|
std::vector<std::string> parents;
|
||||||
try {
|
try {
|
||||||
parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
|
parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
|
||||||
detail::process_quoted_string(name);
|
detail::process_quoted_string(name, '"', '\'', true);
|
||||||
// clean up quotes on the items and check for escaped strings
|
// clean up quotes on the items and check for escaped strings
|
||||||
for(auto &it : items_buffer) {
|
for(auto &it : items_buffer) {
|
||||||
detail::process_quoted_string(it, stringQuote, literalQuote);
|
detail::process_quoted_string(it, stringQuote, literalQuote);
|
||||||
@@ -556,6 +559,31 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
|
|||||||
if(results.size() > 1 && opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Reverse) {
|
if(results.size() > 1 && opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Reverse) {
|
||||||
std::reverse(results.begin(), results.end());
|
std::reverse(results.begin(), results.end());
|
||||||
}
|
}
|
||||||
|
if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum && opt->count() >= 1 &&
|
||||||
|
results.size() == 1) {
|
||||||
|
// if the multi option policy is sum then there is a possibility of incorrect fields being produced
|
||||||
|
// best to just use the original data for config files
|
||||||
|
auto pos = opt->_validate(results[0], 0);
|
||||||
|
if(!pos.empty()) {
|
||||||
|
results = opt->results();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Join && opt->count() > 1) {
|
||||||
|
char delim = opt->get_delimiter();
|
||||||
|
if(delim == '\0') {
|
||||||
|
// this branch deals with a situation where the output would not be readable by a config file
|
||||||
|
results = opt->results();
|
||||||
|
} else {
|
||||||
|
// this branch deals with the case of the strings containing the delimiter itself or empty
|
||||||
|
// strings which would be interpreted incorrectly
|
||||||
|
auto delim_count = std::count(results[0].begin(), results[0].end(), delim);
|
||||||
|
if(results[0].back() == delim ||
|
||||||
|
static_cast<decltype(delim_count)>(opt->count()) < delim_count - 1 ||
|
||||||
|
results[0].find(std::string(2, delim)) != std::string::npos) {
|
||||||
|
results = opt->results();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
std::string value =
|
std::string value =
|
||||||
detail::ini_join(results, arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
|
detail::ini_join(results, arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
|
||||||
|
|
||||||
|
|||||||
@@ -544,13 +544,15 @@ CLI11_INLINE void handle_secondary_array(std::string &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, char literal_char) {
|
CLI11_INLINE bool
|
||||||
|
process_quoted_string(std::string &str, char string_char, char literal_char, bool disable_secondary_array_processing) {
|
||||||
if(str.size() <= 1) {
|
if(str.size() <= 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(detail::is_binary_escaped_string(str)) {
|
if(detail::is_binary_escaped_string(str)) {
|
||||||
str = detail::extract_binary_string(str);
|
str = detail::extract_binary_string(str);
|
||||||
handle_secondary_array(str);
|
if(!disable_secondary_array_processing)
|
||||||
|
handle_secondary_array(str);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(str.front() == string_char && str.back() == string_char) {
|
if(str.front() == string_char && str.back() == string_char) {
|
||||||
@@ -558,12 +560,14 @@ CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, char
|
|||||||
if(str.find_first_of('\\') != std::string::npos) {
|
if(str.find_first_of('\\') != std::string::npos) {
|
||||||
str = detail::remove_escaped_characters(str);
|
str = detail::remove_escaped_characters(str);
|
||||||
}
|
}
|
||||||
handle_secondary_array(str);
|
if(!disable_secondary_array_processing)
|
||||||
|
handle_secondary_array(str);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) {
|
if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) {
|
||||||
detail::remove_outer(str, str.front());
|
detail::remove_outer(str, str.front());
|
||||||
handle_secondary_array(str);
|
if(!disable_secondary_array_processing)
|
||||||
|
handle_secondary_array(str);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ set(CLI11_TESTS
|
|||||||
HelpersTest
|
HelpersTest
|
||||||
ConfigFileTest
|
ConfigFileTest
|
||||||
OptionTypeTest
|
OptionTypeTest
|
||||||
|
NumericTypeTest
|
||||||
SimpleTest
|
SimpleTest
|
||||||
AppTest
|
AppTest
|
||||||
SetTest
|
SetTest
|
||||||
|
|||||||
@@ -309,17 +309,50 @@ TEST_CASE("fuzz_config_modifier_test1") {
|
|||||||
CHECK(opt3->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum);
|
CHECK(opt3->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** test the fuzzer itself to support custom subcommand*/
|
||||||
|
TEST_CASE("fuzz_config_subcommand") {
|
||||||
|
CLI::FuzzApp fuzzdata;
|
||||||
|
auto app = fuzzdata.generateApp();
|
||||||
|
|
||||||
|
std::string config_string =
|
||||||
|
"<subcommand name=sub_custom><option modifiers=R2CG>--new_option</option><flag "
|
||||||
|
"modifiers=cFg>--new_flag</flag><vector modifiers=35s+>--new_vector</vector></subcommand>";
|
||||||
|
auto loc = fuzzdata.add_custom_options(app.get(), config_string);
|
||||||
|
config_string = config_string.substr(loc);
|
||||||
|
CHECK(config_string.empty());
|
||||||
|
auto *sub_c = app->get_subcommand("sub_custom");
|
||||||
|
auto *opt1 = sub_c->get_option_no_throw("--new_option");
|
||||||
|
REQUIRE(opt1 != nullptr);
|
||||||
|
CHECK(opt1->get_required());
|
||||||
|
CHECK(opt1->get_expected_min() == 2);
|
||||||
|
CHECK(opt1->get_configurable());
|
||||||
|
CHECK(opt1->get_ignore_case());
|
||||||
|
auto *opt2 = sub_c->get_option_no_throw("--new_flag");
|
||||||
|
REQUIRE(opt2 != nullptr);
|
||||||
|
CHECK(opt2->get_disable_flag_override());
|
||||||
|
CHECK(!opt2->get_configurable());
|
||||||
|
CHECK(!opt2->get_ignore_case());
|
||||||
|
auto *opt3 = sub_c->get_option_no_throw("--new_vector");
|
||||||
|
REQUIRE(opt3 != nullptr);
|
||||||
|
CHECK(opt3->get_expected_min() == 0);
|
||||||
|
CHECK(opt3->get_expected_max() == 3);
|
||||||
|
CHECK(opt3->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum);
|
||||||
|
}
|
||||||
|
|
||||||
// this test enables the custom option creation operation
|
// this test enables the custom option creation operation
|
||||||
TEST_CASE("app_roundtrip_custom") {
|
TEST_CASE("app_roundtrip_custom") {
|
||||||
CLI::FuzzApp fuzzdata;
|
CLI::FuzzApp fuzzdata;
|
||||||
CLI::FuzzApp fuzzdata2;
|
CLI::FuzzApp fuzzdata2;
|
||||||
auto app = fuzzdata.generateApp();
|
auto app = fuzzdata.generateApp();
|
||||||
auto app2 = fuzzdata2.generateApp();
|
auto app2 = fuzzdata2.generateApp();
|
||||||
int index = GENERATE(range(1, 13));
|
int index = GENERATE(range(1, 24));
|
||||||
|
|
||||||
auto parseData = loadFailureFile("round_trip_custom", index);
|
auto parseData = loadFailureFile("round_trip_custom", index);
|
||||||
|
|
||||||
std::size_t pstring_start{0};
|
std::size_t pstring_start{0};
|
||||||
pstring_start = fuzzdata.add_custom_options(app.get(), parseData);
|
pstring_start = fuzzdata.add_custom_options(app.get(), parseData);
|
||||||
|
INFO("Failure in test case " << index << " file length=" << parseData.size() << " pstring start at "
|
||||||
|
<< pstring_start)
|
||||||
if(pstring_start > 0) {
|
if(pstring_start > 0) {
|
||||||
app->parse(parseData.substr(pstring_start));
|
app->parse(parseData.substr(pstring_start));
|
||||||
CHECK_NOTHROW(app->help("", CLI::AppFormatMode::All));
|
CHECK_NOTHROW(app->help("", CLI::AppFormatMode::All));
|
||||||
@@ -333,14 +366,13 @@ TEST_CASE("app_roundtrip_custom") {
|
|||||||
if(pstring_start > 0) {
|
if(pstring_start > 0) {
|
||||||
fuzzdata2.add_custom_options(app2.get(), parseData);
|
fuzzdata2.add_custom_options(app2.get(), parseData);
|
||||||
}
|
}
|
||||||
app2->parse_from_stream(out);
|
CHECK_NOTHROW(app2->parse_from_stream(out));
|
||||||
auto result = fuzzdata2.compare(fuzzdata);
|
auto result = fuzzdata2.compare(fuzzdata);
|
||||||
if(!result) {
|
if(!result) {
|
||||||
result = fuzzdata.compare(fuzzdata2, true);
|
result = fuzzdata.compare(fuzzdata2, true);
|
||||||
std::cout << "\n:parsed:\n" << parseData;
|
std::cout << "\n:parsed:\n" << parseData;
|
||||||
std::cout << "\n:config:\n" << configOut << '\n';
|
std::cout << "\n:config:\n" << configOut << '\n';
|
||||||
}
|
}
|
||||||
INFO("Failure in test case " << index)
|
|
||||||
CHECK(result);
|
CHECK(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,7 +383,7 @@ TEST_CASE("app_roundtrip_parse_normal_fail") {
|
|||||||
// like HorribleErrors
|
// like HorribleErrors
|
||||||
CLI::FuzzApp fuzzdata;
|
CLI::FuzzApp fuzzdata;
|
||||||
auto app = fuzzdata.generateApp();
|
auto app = fuzzdata.generateApp();
|
||||||
int index = GENERATE(range(1, 7));
|
int index = GENERATE(range(1, 11));
|
||||||
auto parseData = loadFailureFile("parse_fail_check", index);
|
auto parseData = loadFailureFile("parse_fail_check", index);
|
||||||
std::size_t pstring_start{0};
|
std::size_t pstring_start{0};
|
||||||
try {
|
try {
|
||||||
|
|||||||
412
tests/NumericTypeTest.cpp
Normal file
412
tests/NumericTypeTest.cpp
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
// Copyright (c) 2017-2025, 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 "catch.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <complex>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <deque>
|
||||||
|
#include <forward_list>
|
||||||
|
#include <limits>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using Catch::Matchers::WithinRel;
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleFunction", "[optiontype]") {
|
||||||
|
double res{0.0};
|
||||||
|
app.add_option_function<double>("--val", [&res](double val) { res = std::abs(val + 54); });
|
||||||
|
args = {"--val", "-354.356"};
|
||||||
|
run();
|
||||||
|
CHECK_THAT(res, WithinRel(300.356));
|
||||||
|
// get the original value as entered as an integer
|
||||||
|
CHECK_THAT(app["--val"]->as<float>(), WithinRel(-354.356f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleFunctionFail", "[optiontype]") {
|
||||||
|
double res = NAN;
|
||||||
|
app.add_option_function<double>("--val", [&res](double val) { res = std::abs(val + 54); });
|
||||||
|
args = {"--val", "not_double"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleVectorFunction", "[optiontype]") {
|
||||||
|
std::vector<double> res;
|
||||||
|
app.add_option_function<std::vector<double>>("--val", [&res](const std::vector<double> &val) {
|
||||||
|
res = val;
|
||||||
|
std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
|
||||||
|
});
|
||||||
|
args = {"--val", "5", "--val", "6", "--val", "7"};
|
||||||
|
run();
|
||||||
|
CHECK(3u == res.size());
|
||||||
|
CHECK_THAT(res[0], WithinRel(10.0));
|
||||||
|
CHECK_THAT(res[2], WithinRel(12.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleVectorFunctionFail", "[optiontype]") {
|
||||||
|
std::vector<double> res;
|
||||||
|
std::string vstring = "--val";
|
||||||
|
app.add_option_function<std::vector<double>>(vstring, [&res](const std::vector<double> &val) {
|
||||||
|
res = val;
|
||||||
|
std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
|
||||||
|
});
|
||||||
|
args = {"--val", "five", "--val", "nine", "--val", "7"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
// check that getting the results through the results function generates the same error
|
||||||
|
CHECK_THROWS_AS(app[vstring]->results(res), CLI::ConversionError);
|
||||||
|
auto strvec = app[vstring]->as<std::vector<std::string>>();
|
||||||
|
CHECK(3u == strvec.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleVectorFunctionRunCallbackOnDefault", "[optiontype]") {
|
||||||
|
std::vector<double> res;
|
||||||
|
auto *opt = app.add_option_function<std::vector<double>>("--val", [&res](const std::vector<double> &val) {
|
||||||
|
res = val;
|
||||||
|
std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
|
||||||
|
});
|
||||||
|
args = {"--val", "5", "--val", "6", "--val", "7"};
|
||||||
|
run();
|
||||||
|
CHECK(3u == res.size());
|
||||||
|
CHECK(10.0 == res[0]);
|
||||||
|
CHECK(12.0 == res[2]);
|
||||||
|
CHECK(!opt->get_run_callback_for_default());
|
||||||
|
opt->run_callback_for_default();
|
||||||
|
opt->default_val(std::vector<int>{2, 1, -2});
|
||||||
|
CHECK(7.0 == res[0]);
|
||||||
|
CHECK(3.0 == res[2]);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(opt->default_val("this is a string"), CLI::ConversionError);
|
||||||
|
auto vec = opt->as<std::vector<double>>();
|
||||||
|
REQUIRE(3U == vec.size());
|
||||||
|
CHECK(5.0 == vec[0]);
|
||||||
|
CHECK(7.0 == vec[2]);
|
||||||
|
opt->check(CLI::Number);
|
||||||
|
opt->run_callback_for_default(false);
|
||||||
|
CHECK_THROWS_AS(opt->default_val("this is a string"), CLI::ValidationError);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::map<std::string, double> testValuesDouble{
|
||||||
|
{"3.14159", 3.14159},
|
||||||
|
{"-3.14159", -3.14159},
|
||||||
|
{"-3.14159\t", -3.14159},
|
||||||
|
{"-3.14159 ", -3.14159},
|
||||||
|
{"+1.0", 1.0},
|
||||||
|
{"-0.01", -0.01},
|
||||||
|
{"-.01", -0.01},
|
||||||
|
{"-.3251", -0.3251},
|
||||||
|
{"+.3251", 0.3251},
|
||||||
|
{"5e22", 5e22},
|
||||||
|
{" 5e22", 5e22},
|
||||||
|
{" 5e22 ", 5e22},
|
||||||
|
{"-2E-2", -2e-2},
|
||||||
|
{"5e+22", 5e22},
|
||||||
|
{"1e06", 1e6},
|
||||||
|
{"6.626e-34", 6.626e-34},
|
||||||
|
{"6.626e+34", 6.626e34},
|
||||||
|
{"-6.626e-34", -6.626e-34},
|
||||||
|
{"224_617.445_991", 224617.445991},
|
||||||
|
{"224'617.445'991", 224617.445991},
|
||||||
|
{"inf", std::numeric_limits<double>::infinity()},
|
||||||
|
{"+inf", std::numeric_limits<double>::infinity()},
|
||||||
|
{"-inf", -std::numeric_limits<double>::infinity()},
|
||||||
|
{"nan", std::numeric_limits<double>::signaling_NaN()},
|
||||||
|
{"+nan", std::numeric_limits<double>::signaling_NaN()},
|
||||||
|
{"-nan", -std::numeric_limits<double>::signaling_NaN()},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "floatingConversions", "[optiontype]") {
|
||||||
|
auto test_data = GENERATE(from_range(testValuesDouble));
|
||||||
|
|
||||||
|
double val{0};
|
||||||
|
app.add_option("--val", val);
|
||||||
|
|
||||||
|
args = {"--val", test_data.first};
|
||||||
|
|
||||||
|
run();
|
||||||
|
if(std::isnan(test_data.second)) {
|
||||||
|
CHECK(std::isnan(val));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
CHECK_THAT(val, WithinRel(test_data.second, 1e-11));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::map<std::string, std::int64_t> testValuesInt{
|
||||||
|
{"+99", 99},
|
||||||
|
{"99", 99},
|
||||||
|
{"-99", -99},
|
||||||
|
{"-99 ", -99},
|
||||||
|
{"0xDEADBEEF", 0xDEADBEEF},
|
||||||
|
{"0xdeadbeef", 0xDEADBEEF},
|
||||||
|
{"0XDEADBEEF", 0xDEADBEEF},
|
||||||
|
{"0Xdeadbeef", 0xDEADBEEF},
|
||||||
|
{"0xdead_beef", 0xDEADBEEF},
|
||||||
|
{"0xdead'beef", 0xDEADBEEF},
|
||||||
|
{"0o01234567", 001234567},
|
||||||
|
{"0o755", 0755},
|
||||||
|
{"0755", 0755},
|
||||||
|
{"995862_262", 995862262},
|
||||||
|
{"995862262", 995862262},
|
||||||
|
{"-995862275", -995862275},
|
||||||
|
{"\t-995862275\t", -995862275},
|
||||||
|
{"-995'862'275", -995862275},
|
||||||
|
{"0b11010110", 0xD6},
|
||||||
|
{"0b1101'0110", 0xD6},
|
||||||
|
{"0B11010110", 0xD6},
|
||||||
|
{"0B1101'0110", 0xD6},
|
||||||
|
{"1_2_3_4_5", 12345},
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "intConversions", "[optiontype]") {
|
||||||
|
|
||||||
|
auto test_data = GENERATE(from_range(testValuesInt));
|
||||||
|
|
||||||
|
std::int64_t val{0};
|
||||||
|
app.add_option("--val", val);
|
||||||
|
|
||||||
|
args = {"--val", test_data.first};
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
CHECK(val == test_data.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "intConversionsErange", "[optiontype]") {
|
||||||
|
|
||||||
|
std::int64_t val{0};
|
||||||
|
app.add_option("--val", val);
|
||||||
|
|
||||||
|
args = {"--val", "0o11545241241415151512312415123125667"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ParseError);
|
||||||
|
|
||||||
|
args = {"--val", "0b1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ParseError);
|
||||||
|
|
||||||
|
args = {"--val", "0B1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ParseError);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::map<std::string, std::uint64_t> testValuesUInt{
|
||||||
|
{"+99", 99},
|
||||||
|
{"99", 99},
|
||||||
|
{" 99 ", 99},
|
||||||
|
{"0xDEADBEEF", 0xDEADBEEF},
|
||||||
|
{"0xdeadbeef", 0xDEADBEEF},
|
||||||
|
{"0XDEADBEEF", 0xDEADBEEF},
|
||||||
|
{"0Xdeadbeef", 0xDEADBEEF},
|
||||||
|
{"0xdead_beef", 0xDEADBEEF},
|
||||||
|
{"0xdead'beef", 0xDEADBEEF},
|
||||||
|
{"0o01234567", 001234567},
|
||||||
|
{"0o755", 0755},
|
||||||
|
{"0o755\t", 0755},
|
||||||
|
{"0755", 0755},
|
||||||
|
{"995862_262", 995862262},
|
||||||
|
{"995862262", 995862262},
|
||||||
|
{"+995862275", +995862275},
|
||||||
|
{"+995862275 \n\t", +995862275},
|
||||||
|
{"995'862'275", 995862275},
|
||||||
|
{"0b11010110", 0xD6},
|
||||||
|
{"0b1101'0110", 0xD6},
|
||||||
|
{"0b1101'0110 ", 0xD6},
|
||||||
|
{"0B11010110", 0xD6},
|
||||||
|
{"0B1101'0110", 0xD6},
|
||||||
|
{"1_2_3_4_5", 12345},
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "uintConversions", "[optiontype]") {
|
||||||
|
|
||||||
|
auto test_data = GENERATE(from_range(testValuesUInt));
|
||||||
|
|
||||||
|
std::uint64_t val{0};
|
||||||
|
app.add_option("--val", val);
|
||||||
|
|
||||||
|
args = {"--val", test_data.first};
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
CHECK(val == test_data.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "uintConversionsErange", "[optiontype]") {
|
||||||
|
|
||||||
|
std::uint64_t val{0};
|
||||||
|
app.add_option("--val", val);
|
||||||
|
|
||||||
|
args = {"--val", "0o11545241241415151512312415123125667"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ParseError);
|
||||||
|
|
||||||
|
args = {"--val", "0b1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ParseError);
|
||||||
|
|
||||||
|
args = {"--val", "0B1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ParseError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "CharOption", "[optiontype]") {
|
||||||
|
char c1{'t'};
|
||||||
|
app.add_option("-c", c1);
|
||||||
|
|
||||||
|
args = {"-c", "g"};
|
||||||
|
run();
|
||||||
|
CHECK('g' == c1);
|
||||||
|
|
||||||
|
args = {"-c", "1"};
|
||||||
|
run();
|
||||||
|
CHECK('1' == c1);
|
||||||
|
|
||||||
|
args = {"-c", "77"};
|
||||||
|
run();
|
||||||
|
CHECK(77 == c1);
|
||||||
|
|
||||||
|
// convert hex for digit
|
||||||
|
args = {"-c", "0x44"};
|
||||||
|
run();
|
||||||
|
CHECK(0x44 == c1);
|
||||||
|
|
||||||
|
args = {"-c", "751615654161688126132138844896646748852"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "IntegerOverFlowShort", "[optiontype]") {
|
||||||
|
std::int16_t A{0};
|
||||||
|
std::uint16_t B{0};
|
||||||
|
|
||||||
|
app.add_option("-a", A);
|
||||||
|
app.add_option("-b", B);
|
||||||
|
|
||||||
|
args = {"-a", "2626254242"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "2626254242"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-26262"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-262624262525"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "IntegerOverFlowInt", "[optiontype]") {
|
||||||
|
int A{0};
|
||||||
|
unsigned int B{0};
|
||||||
|
|
||||||
|
app.add_option("-a", A);
|
||||||
|
app.add_option("-b", B);
|
||||||
|
|
||||||
|
args = {"-a", "262625424225252"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "262625424225252"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-2626225252"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-26262426252525252"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "IntegerOverFlowLong", "[optiontype]") {
|
||||||
|
std::int32_t A{0};
|
||||||
|
std::uint32_t B{0};
|
||||||
|
|
||||||
|
app.add_option("-a", A);
|
||||||
|
app.add_option("-b", B);
|
||||||
|
|
||||||
|
args = {"-a", "1111111111111111111111111111"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "1111111111111111111111111111"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-2626225252"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-111111111111111111111111"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "IntegerOverFlowLongLong", "[optiontype]") {
|
||||||
|
std::int64_t A{0};
|
||||||
|
std::uint64_t B{0};
|
||||||
|
|
||||||
|
app.add_option("-a", A);
|
||||||
|
app.add_option("-b", B);
|
||||||
|
|
||||||
|
args = {"-a", "1111111111111111111111111111111111111111111111111111111111"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "1111111111111111111111111111111111111111111111111111111111"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-2626225252"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
|
||||||
|
args = {"-b", "-111111111111111111111111111111111111111111111111111111111"};
|
||||||
|
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now with tuple support this is possible
|
||||||
|
TEST_CASE_METHOD(TApp, "floatPair", "[optiontype]") {
|
||||||
|
|
||||||
|
std::pair<float, float> custom_opt;
|
||||||
|
|
||||||
|
auto *opt = app.add_option("--fp", custom_opt)->delimiter(',');
|
||||||
|
opt->default_str("3.4,2.7");
|
||||||
|
|
||||||
|
args = {"--fp", "12", "1.5"};
|
||||||
|
|
||||||
|
run();
|
||||||
|
CHECK(12.0f == Approx(custom_opt.first));
|
||||||
|
CHECK(1.5f == Approx(custom_opt.second));
|
||||||
|
args = {};
|
||||||
|
opt->force_callback();
|
||||||
|
run();
|
||||||
|
CHECK(3.4f == Approx(custom_opt.first));
|
||||||
|
CHECK(2.7f == Approx(custom_opt.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
// now with tuple support this is possible
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleVector", "[optiontype]") {
|
||||||
|
|
||||||
|
std::vector<double> custom_opt;
|
||||||
|
|
||||||
|
app.add_option("--fp", custom_opt);
|
||||||
|
|
||||||
|
args = {"--fp", "12.7", "1.5"};
|
||||||
|
run();
|
||||||
|
CHECK(12.7 == Approx(custom_opt[0]));
|
||||||
|
CHECK(1.5 == Approx(custom_opt[1]));
|
||||||
|
args = {"--fp", "12.7", "-.5"};
|
||||||
|
run();
|
||||||
|
CHECK(12.7 == Approx(custom_opt[0]));
|
||||||
|
CHECK(-0.5 == Approx(custom_opt[1]));
|
||||||
|
|
||||||
|
args = {"--fp", "-.7", "+.5"};
|
||||||
|
run();
|
||||||
|
CHECK(-0.7 == Approx(custom_opt[0]));
|
||||||
|
CHECK(0.5 == Approx(custom_opt[1]));
|
||||||
|
}
|
||||||
@@ -51,78 +51,6 @@ TEST_CASE_METHOD(TApp, "OneStringFunction", "[optiontype]") {
|
|||||||
CHECK("mystring" == str);
|
CHECK("mystring" == str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "doubleFunction", "[optiontype]") {
|
|
||||||
double res{0.0};
|
|
||||||
app.add_option_function<double>("--val", [&res](double val) { res = std::abs(val + 54); });
|
|
||||||
args = {"--val", "-354.356"};
|
|
||||||
run();
|
|
||||||
CHECK_THAT(res, WithinRel(300.356));
|
|
||||||
// get the original value as entered as an integer
|
|
||||||
CHECK_THAT(app["--val"]->as<float>(), WithinRel(-354.356f));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "doubleFunctionFail", "[optiontype]") {
|
|
||||||
double res = NAN;
|
|
||||||
app.add_option_function<double>("--val", [&res](double val) { res = std::abs(val + 54); });
|
|
||||||
args = {"--val", "not_double"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "doubleVectorFunction", "[optiontype]") {
|
|
||||||
std::vector<double> res;
|
|
||||||
app.add_option_function<std::vector<double>>("--val", [&res](const std::vector<double> &val) {
|
|
||||||
res = val;
|
|
||||||
std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
|
|
||||||
});
|
|
||||||
args = {"--val", "5", "--val", "6", "--val", "7"};
|
|
||||||
run();
|
|
||||||
CHECK(3u == res.size());
|
|
||||||
CHECK_THAT(res[0], WithinRel(10.0));
|
|
||||||
CHECK_THAT(res[2], WithinRel(12.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "doubleVectorFunctionFail", "[optiontype]") {
|
|
||||||
std::vector<double> res;
|
|
||||||
std::string vstring = "--val";
|
|
||||||
app.add_option_function<std::vector<double>>(vstring, [&res](const std::vector<double> &val) {
|
|
||||||
res = val;
|
|
||||||
std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
|
|
||||||
});
|
|
||||||
args = {"--val", "five", "--val", "nine", "--val", "7"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
// check that getting the results through the results function generates the same error
|
|
||||||
CHECK_THROWS_AS(app[vstring]->results(res), CLI::ConversionError);
|
|
||||||
auto strvec = app[vstring]->as<std::vector<std::string>>();
|
|
||||||
CHECK(3u == strvec.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "doubleVectorFunctionRunCallbackOnDefault", "[optiontype]") {
|
|
||||||
std::vector<double> res;
|
|
||||||
auto *opt = app.add_option_function<std::vector<double>>("--val", [&res](const std::vector<double> &val) {
|
|
||||||
res = val;
|
|
||||||
std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
|
|
||||||
});
|
|
||||||
args = {"--val", "5", "--val", "6", "--val", "7"};
|
|
||||||
run();
|
|
||||||
CHECK(3u == res.size());
|
|
||||||
CHECK(10.0 == res[0]);
|
|
||||||
CHECK(12.0 == res[2]);
|
|
||||||
CHECK(!opt->get_run_callback_for_default());
|
|
||||||
opt->run_callback_for_default();
|
|
||||||
opt->default_val(std::vector<int>{2, 1, -2});
|
|
||||||
CHECK(7.0 == res[0]);
|
|
||||||
CHECK(3.0 == res[2]);
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(opt->default_val("this is a string"), CLI::ConversionError);
|
|
||||||
auto vec = opt->as<std::vector<double>>();
|
|
||||||
REQUIRE(3U == vec.size());
|
|
||||||
CHECK(5.0 == vec[0]);
|
|
||||||
CHECK(7.0 == vec[2]);
|
|
||||||
opt->check(CLI::Number);
|
|
||||||
opt->run_callback_for_default(false);
|
|
||||||
CHECK_THROWS_AS(opt->default_val("this is a string"), CLI::ValidationError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "BoolAndIntFlags", "[optiontype]") {
|
TEST_CASE_METHOD(TApp, "BoolAndIntFlags", "[optiontype]") {
|
||||||
|
|
||||||
bool bflag{false};
|
bool bflag{false};
|
||||||
@@ -225,195 +153,6 @@ TEST_CASE_METHOD(TApp, "atomic_int_option", "[optiontype]") {
|
|||||||
CHECK(0 == i);
|
CHECK(0 == i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::map<std::string, double> testValuesDouble{
|
|
||||||
{"3.14159", 3.14159},
|
|
||||||
{"-3.14159", -3.14159},
|
|
||||||
{"-3.14159\t", -3.14159},
|
|
||||||
{"-3.14159 ", -3.14159},
|
|
||||||
{"+1.0", 1.0},
|
|
||||||
{"-0.01", -0.01},
|
|
||||||
{"-.01", -0.01},
|
|
||||||
{"-.3251", -0.3251},
|
|
||||||
{"+.3251", 0.3251},
|
|
||||||
{"5e22", 5e22},
|
|
||||||
{" 5e22", 5e22},
|
|
||||||
{" 5e22 ", 5e22},
|
|
||||||
{"-2E-2", -2e-2},
|
|
||||||
{"5e+22", 5e22},
|
|
||||||
{"1e06", 1e6},
|
|
||||||
{"6.626e-34", 6.626e-34},
|
|
||||||
{"6.626e+34", 6.626e34},
|
|
||||||
{"-6.626e-34", -6.626e-34},
|
|
||||||
{"224_617.445_991", 224617.445991},
|
|
||||||
{"224'617.445'991", 224617.445991},
|
|
||||||
{"inf", std::numeric_limits<double>::infinity()},
|
|
||||||
{"+inf", std::numeric_limits<double>::infinity()},
|
|
||||||
{"-inf", -std::numeric_limits<double>::infinity()},
|
|
||||||
{"nan", std::numeric_limits<double>::signaling_NaN()},
|
|
||||||
{"+nan", std::numeric_limits<double>::signaling_NaN()},
|
|
||||||
{"-nan", -std::numeric_limits<double>::signaling_NaN()},
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "floatingConversions", "[optiontype]") {
|
|
||||||
auto test_data = GENERATE(from_range(testValuesDouble));
|
|
||||||
|
|
||||||
double val{0};
|
|
||||||
app.add_option("--val", val);
|
|
||||||
|
|
||||||
args = {"--val", test_data.first};
|
|
||||||
|
|
||||||
run();
|
|
||||||
if(std::isnan(test_data.second)) {
|
|
||||||
CHECK(std::isnan(val));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
CHECK_THAT(val, WithinRel(test_data.second, 1e-11));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::map<std::string, std::int64_t> testValuesInt{
|
|
||||||
{"+99", 99},
|
|
||||||
{"99", 99},
|
|
||||||
{"-99", -99},
|
|
||||||
{"-99 ", -99},
|
|
||||||
{"0xDEADBEEF", 0xDEADBEEF},
|
|
||||||
{"0xdeadbeef", 0xDEADBEEF},
|
|
||||||
{"0XDEADBEEF", 0xDEADBEEF},
|
|
||||||
{"0Xdeadbeef", 0xDEADBEEF},
|
|
||||||
{"0xdead_beef", 0xDEADBEEF},
|
|
||||||
{"0xdead'beef", 0xDEADBEEF},
|
|
||||||
{"0o01234567", 001234567},
|
|
||||||
{"0o755", 0755},
|
|
||||||
{"0755", 0755},
|
|
||||||
{"995862_262", 995862262},
|
|
||||||
{"995862262", 995862262},
|
|
||||||
{"-995862275", -995862275},
|
|
||||||
{"\t-995862275\t", -995862275},
|
|
||||||
{"-995'862'275", -995862275},
|
|
||||||
{"0b11010110", 0xD6},
|
|
||||||
{"0b1101'0110", 0xD6},
|
|
||||||
{"0B11010110", 0xD6},
|
|
||||||
{"0B1101'0110", 0xD6},
|
|
||||||
{"1_2_3_4_5", 12345},
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "intConversions", "[optiontype]") {
|
|
||||||
|
|
||||||
auto test_data = GENERATE(from_range(testValuesInt));
|
|
||||||
|
|
||||||
std::int64_t val{0};
|
|
||||||
app.add_option("--val", val);
|
|
||||||
|
|
||||||
args = {"--val", test_data.first};
|
|
||||||
|
|
||||||
run();
|
|
||||||
|
|
||||||
CHECK(val == test_data.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "intConversionsErange", "[optiontype]") {
|
|
||||||
|
|
||||||
std::int64_t val{0};
|
|
||||||
app.add_option("--val", val);
|
|
||||||
|
|
||||||
args = {"--val", "0o11545241241415151512312415123125667"};
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ParseError);
|
|
||||||
|
|
||||||
args = {"--val", "0b1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ParseError);
|
|
||||||
|
|
||||||
args = {"--val", "0B1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ParseError);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::map<std::string, std::uint64_t> testValuesUInt{
|
|
||||||
{"+99", 99},
|
|
||||||
{"99", 99},
|
|
||||||
{" 99 ", 99},
|
|
||||||
{"0xDEADBEEF", 0xDEADBEEF},
|
|
||||||
{"0xdeadbeef", 0xDEADBEEF},
|
|
||||||
{"0XDEADBEEF", 0xDEADBEEF},
|
|
||||||
{"0Xdeadbeef", 0xDEADBEEF},
|
|
||||||
{"0xdead_beef", 0xDEADBEEF},
|
|
||||||
{"0xdead'beef", 0xDEADBEEF},
|
|
||||||
{"0o01234567", 001234567},
|
|
||||||
{"0o755", 0755},
|
|
||||||
{"0o755\t", 0755},
|
|
||||||
{"0755", 0755},
|
|
||||||
{"995862_262", 995862262},
|
|
||||||
{"995862262", 995862262},
|
|
||||||
{"+995862275", +995862275},
|
|
||||||
{"+995862275 \n\t", +995862275},
|
|
||||||
{"995'862'275", 995862275},
|
|
||||||
{"0b11010110", 0xD6},
|
|
||||||
{"0b1101'0110", 0xD6},
|
|
||||||
{"0b1101'0110 ", 0xD6},
|
|
||||||
{"0B11010110", 0xD6},
|
|
||||||
{"0B1101'0110", 0xD6},
|
|
||||||
{"1_2_3_4_5", 12345},
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "uintConversions", "[optiontype]") {
|
|
||||||
|
|
||||||
auto test_data = GENERATE(from_range(testValuesUInt));
|
|
||||||
|
|
||||||
std::uint64_t val{0};
|
|
||||||
app.add_option("--val", val);
|
|
||||||
|
|
||||||
args = {"--val", test_data.first};
|
|
||||||
|
|
||||||
run();
|
|
||||||
|
|
||||||
CHECK(val == test_data.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "uintConversionsErange", "[optiontype]") {
|
|
||||||
|
|
||||||
std::uint64_t val{0};
|
|
||||||
app.add_option("--val", val);
|
|
||||||
|
|
||||||
args = {"--val", "0o11545241241415151512312415123125667"};
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ParseError);
|
|
||||||
|
|
||||||
args = {"--val", "0b1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ParseError);
|
|
||||||
|
|
||||||
args = {"--val", "0B1011000001101011001100110011111000101010101011111111111111111111111001010111011100"};
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ParseError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "CharOption", "[optiontype]") {
|
|
||||||
char c1{'t'};
|
|
||||||
app.add_option("-c", c1);
|
|
||||||
|
|
||||||
args = {"-c", "g"};
|
|
||||||
run();
|
|
||||||
CHECK('g' == c1);
|
|
||||||
|
|
||||||
args = {"-c", "1"};
|
|
||||||
run();
|
|
||||||
CHECK('1' == c1);
|
|
||||||
|
|
||||||
args = {"-c", "77"};
|
|
||||||
run();
|
|
||||||
CHECK(77 == c1);
|
|
||||||
|
|
||||||
// convert hex for digit
|
|
||||||
args = {"-c", "0x44"};
|
|
||||||
run();
|
|
||||||
CHECK(0x44 == c1);
|
|
||||||
|
|
||||||
args = {"-c", "751615654161688126132138844896646748852"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "vectorDefaults", "[optiontype]") {
|
TEST_CASE_METHOD(TApp, "vectorDefaults", "[optiontype]") {
|
||||||
std::vector<int> vals{4, 5};
|
std::vector<int> vals{4, 5};
|
||||||
auto *opt = app.add_option("--long", vals)->capture_default_str();
|
auto *opt = app.add_option("--long", vals)->capture_default_str();
|
||||||
@@ -606,86 +345,6 @@ TEST_CASE_METHOD(TApp, "VectorIndexedValidator", "[optiontype]") {
|
|||||||
CHECK_THROWS_AS(run(), CLI::ValidationError);
|
CHECK_THROWS_AS(run(), CLI::ValidationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "IntegerOverFlowShort", "[optiontype]") {
|
|
||||||
std::int16_t A{0};
|
|
||||||
std::uint16_t B{0};
|
|
||||||
|
|
||||||
app.add_option("-a", A);
|
|
||||||
app.add_option("-b", B);
|
|
||||||
|
|
||||||
args = {"-a", "2626254242"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "2626254242"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-26262"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-262624262525"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "IntegerOverFlowInt", "[optiontype]") {
|
|
||||||
int A{0};
|
|
||||||
unsigned int B{0};
|
|
||||||
|
|
||||||
app.add_option("-a", A);
|
|
||||||
app.add_option("-b", B);
|
|
||||||
|
|
||||||
args = {"-a", "262625424225252"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "262625424225252"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-2626225252"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-26262426252525252"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "IntegerOverFlowLong", "[optiontype]") {
|
|
||||||
std::int32_t A{0};
|
|
||||||
std::uint32_t B{0};
|
|
||||||
|
|
||||||
app.add_option("-a", A);
|
|
||||||
app.add_option("-b", B);
|
|
||||||
|
|
||||||
args = {"-a", "1111111111111111111111111111"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "1111111111111111111111111111"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-2626225252"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-111111111111111111111111"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "IntegerOverFlowLongLong", "[optiontype]") {
|
|
||||||
std::int64_t A{0};
|
|
||||||
std::uint64_t B{0};
|
|
||||||
|
|
||||||
app.add_option("-a", A);
|
|
||||||
app.add_option("-b", B);
|
|
||||||
|
|
||||||
args = {"-a", "1111111111111111111111111111111111111111111111111111111111"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "1111111111111111111111111111111111111111111111111111111111"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-2626225252"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
|
|
||||||
args = {"-b", "-111111111111111111111111111111111111111111111111111111111"};
|
|
||||||
CHECK_THROWS_AS(run(), CLI::ConversionError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "VectorUnlimString", "[optiontype]") {
|
TEST_CASE_METHOD(TApp, "VectorUnlimString", "[optiontype]") {
|
||||||
std::vector<std::string> strvec;
|
std::vector<std::string> strvec;
|
||||||
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
||||||
@@ -805,48 +464,6 @@ TEST_CASE_METHOD(TApp, "CustomDoubleOptionAlt", "[optiontype]") {
|
|||||||
CHECK(1.5 == Approx(custom_opt.second));
|
CHECK(1.5 == Approx(custom_opt.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
// now with tuple support this is possible
|
|
||||||
TEST_CASE_METHOD(TApp, "floatPair", "[optiontype]") {
|
|
||||||
|
|
||||||
std::pair<float, float> custom_opt;
|
|
||||||
|
|
||||||
auto *opt = app.add_option("--fp", custom_opt)->delimiter(',');
|
|
||||||
opt->default_str("3.4,2.7");
|
|
||||||
|
|
||||||
args = {"--fp", "12", "1.5"};
|
|
||||||
|
|
||||||
run();
|
|
||||||
CHECK(12.0f == Approx(custom_opt.first));
|
|
||||||
CHECK(1.5f == Approx(custom_opt.second));
|
|
||||||
args = {};
|
|
||||||
opt->force_callback();
|
|
||||||
run();
|
|
||||||
CHECK(3.4f == Approx(custom_opt.first));
|
|
||||||
CHECK(2.7f == Approx(custom_opt.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
// now with tuple support this is possible
|
|
||||||
TEST_CASE_METHOD(TApp, "doubleVector", "[optiontype]") {
|
|
||||||
|
|
||||||
std::vector<double> custom_opt;
|
|
||||||
|
|
||||||
app.add_option("--fp", custom_opt);
|
|
||||||
|
|
||||||
args = {"--fp", "12.7", "1.5"};
|
|
||||||
run();
|
|
||||||
CHECK(12.7 == Approx(custom_opt[0]));
|
|
||||||
CHECK(1.5 == Approx(custom_opt[1]));
|
|
||||||
args = {"--fp", "12.7", "-.5"};
|
|
||||||
run();
|
|
||||||
CHECK(12.7 == Approx(custom_opt[0]));
|
|
||||||
CHECK(-0.5 == Approx(custom_opt[1]));
|
|
||||||
|
|
||||||
args = {"--fp", "-.7", "+.5"};
|
|
||||||
run();
|
|
||||||
CHECK(-0.7 == Approx(custom_opt[0]));
|
|
||||||
CHECK(0.5 == Approx(custom_opt[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// now with independent type sizes and expected this is possible
|
// now with independent type sizes and expected this is possible
|
||||||
TEST_CASE_METHOD(TApp, "vectorPair", "[optiontype]") {
|
TEST_CASE_METHOD(TApp, "vectorPair", "[optiontype]") {
|
||||||
|
|
||||||
|
|||||||
BIN
tests/fuzzFail/parse_fail_check10
Normal file
BIN
tests/fuzzFail/parse_fail_check10
Normal file
Binary file not shown.
BIN
tests/fuzzFail/parse_fail_check7
Normal file
BIN
tests/fuzzFail/parse_fail_check7
Normal file
Binary file not shown.
BIN
tests/fuzzFail/parse_fail_check8
Normal file
BIN
tests/fuzzFail/parse_fail_check8
Normal file
Binary file not shown.
BIN
tests/fuzzFail/parse_fail_check9
Normal file
BIN
tests/fuzzFail/parse_fail_check9
Normal file
Binary file not shown.
28
tests/fuzzFail/round_trip_custom12
Normal file
28
tests/fuzzFail/round_trip_custom12
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
"--vA=
|
||||||
|
|
||||||
|
vA
|
||||||
|
-
|
||||||
|
=--svtup
|
||||||
|
|
||||||
|
vA=
|
||||||
|
-d'=--svt=-
|
||||||
|
v vA
|
||||||
|
-
|
||||||
|
=-
|
||||||
|
v =--svtup
|
||||||
|
|
||||||
|
vA=
|
||||||
|
-d'=--svtup
|
||||||
|
|
||||||
|
vA--svtup
|
||||||
|
|
||||||
|
vA=
|
||||||
|
-d'=--svtup
|
||||||
|
|
||||||
|
vA=
|
||||||
|
-d'
|
||||||
|
|
||||||
|
=
|
||||||
|
-d'
|
||||||
|
|
||||||
|
""
|
||||||
BIN
tests/fuzzFail/round_trip_custom14
Normal file
BIN
tests/fuzzFail/round_trip_custom14
Normal file
Binary file not shown.
BIN
tests/fuzzFail/round_trip_custom15
Normal file
BIN
tests/fuzzFail/round_trip_custom15
Normal file
Binary file not shown.
BIN
tests/fuzzFail/round_trip_custom16
Normal file
BIN
tests/fuzzFail/round_trip_custom16
Normal file
Binary file not shown.
BIN
tests/fuzzFail/round_trip_custom17
Normal file
BIN
tests/fuzzFail/round_trip_custom17
Normal file
Binary file not shown.
BIN
tests/fuzzFail/round_trip_custom18
Normal file
BIN
tests/fuzzFail/round_trip_custom18
Normal file
Binary file not shown.
BIN
tests/fuzzFail/round_trip_custom19
Normal file
BIN
tests/fuzzFail/round_trip_custom19
Normal file
Binary file not shown.
BIN
tests/fuzzFail/round_trip_custom20
Normal file
BIN
tests/fuzzFail/round_trip_custom20
Normal file
Binary file not shown.
1
tests/fuzzFail/round_trip_custom21
Normal file
1
tests/fuzzFail/round_trip_custom21
Normal file
@@ -0,0 +1 @@
|
|||||||
|
--vSt--vSyt--vSyt
|
||||||
4
tests/fuzzFail/round_trip_custom22
Normal file
4
tests/fuzzFail/round_trip_custom22
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
--vD=`=\
|
||||||
|
-3333pt4=
|
||||||
|
|
||||||
|
--vop= -t=0f3*333334==333333333333333#333333opt1333333333333up23334-aa-ccca333aaaaaa66
|
||||||
BIN
tests/fuzzFail/round_trip_custom23
Normal file
BIN
tests/fuzzFail/round_trip_custom23
Normal file
Binary file not shown.
Reference in New Issue
Block a user