Update ArgManager GetArg helper methods to work better with ALLOW flags

Update GetArg, GetArgs, GetBoolArg, and GetIntArg helper methods to work
conveniently with ALLOW_BOOL, ALLOW_INT, and ALLOW_STRING flags. This commit
does not change application behavior in any way because these flags are new
and currently only used in unit tests.

The GetArg methods are convenience wrappers around the GetSetting method. The
GetSetting method returns the originally parsed settings values in their
declared bool/int/string types, while the GetArg wrappers provide extra
type-coercion and default-value fallback features as additional conveniences
for callers.

This commit makes two changes to GetArg, GetArgs, GetBoolArg, and GetIntArg
helper methods when BOOL/INT/STRING flags are used:

1. GetArg methods will now raise errors if they are called with inconsistent
   flags. For example, GetArgs will raise a logic_error if it is called on a
   non-LIST setting, GetIntArg will raise a logic_error if it is called
   on a non-INT setting.

2. GetArg methods will now avoid various type coersion footguns when they are
   called on new BOOL/INT/STRING settings. Existing ALLOW_ANY settings are
   unaffected. For example, negated settings will return "" empty strings
   instead of "0" strings (in the past the "0" strings caused strangeness like
   "-nowallet" options creating wallet files named "0"). The new behaviors are
   fully specified and checked by the `CheckValueTest` unit test.

The ergonomics of the GetArg helper methods are subjective and the behaviors
they implement can be nitpicked and debated endlessly. But behavior of these
helper methods does not dictate application behavior, and they be bypassed by
calling GetSetting and GetSettingList methods instead. If it's necessary,
behavior of these helper methods can also be changed again in the future.

The changes have no effect on current application behavior because the new
flags are only used in unit tests. The `setting_args` unit test and ALLOW_ANY
checks in the `CheckValueTest` unit test are unchanged and confirm that
`GetArg` methods behave exactly the same (returning the same values and
throwing the same exceptions) for ALLOW_ANY flags before and after this change.
This commit is contained in:
Ryan Ofsky 2022-09-21 15:14:40 -04:00
parent a256f42f79
commit 98d8b63562
4 changed files with 168 additions and 94 deletions

View File

@ -181,6 +181,41 @@ std::optional<common::SettingsValue> InterpretValue(const KeyInfo& key, const st
return std::nullopt;
}
//! Return string if setting is a nonempty string (-setting=abc), "" if setting
//! is false (-nosetting), otherwise return nullopt. For legacy untyped args,
//! coerce bool and number settings to strings as well.
static inline std::optional<std::string> ConvertToString(const common::SettingsValue& value, bool typed_arg)
{
if (value.isStr() && !value.get_str().empty()) return value.get_str();
if (typed_arg && value.isFalse()) return "";
if (!typed_arg && !value.isNull()) {
if (value.isBool()) return value.get_bool() ? "1" : "0";
if (value.isNum()) return value.getValStr();
return value.get_str();
}
return {};
}
//! Return int64 if setting is a number or bool, otherwise return nullopt. For
//! legacy untyped args, coerce string settings as well.
static inline std::optional<int64_t> ConvertToInt(const common::SettingsValue& value, bool typed_arg)
{
if (value.isNum()) return value.getInt<int64_t>();
if (value.isBool()) return value.get_bool();
if (!typed_arg && !value.isNull()) return LocaleIndependentAtoi<int64_t>(value.get_str());
return {};
}
//! Return bool if setting is a bool or number, otherwise return nullopt. For
//! legacy untyped args, coerce strings settings as well.
static inline std::optional<bool> ConvertToBool(const common::SettingsValue& value, bool typed_arg)
{
if (value.isBool()) return value.get_bool();
if (typed_arg && value.isNum()) return value.getInt<int64_t>() != 0;
if (!typed_arg && !value.isNull()) return InterpretBool(value.get_str());
return {};
}
// Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
// #include class definitions for all members.
// For example, m_settings has an internal dependency on univalue.
@ -322,6 +357,29 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
return std::nullopt;
}
/**
* Check that arg has the right flags for use in a given context. Raises
* logic_error if this isn't the case, indicating the argument was registered
* with bad AddArg flags.
*
* Returns true if the arg is registered and has type checking enabled. Returns
* false if the arg was never registered or is untyped.
*/
bool ArgsManager::CheckArgFlags(const std::string& name,
uint32_t require,
uint32_t forbid,
const char* context) const
{
std::optional<unsigned int> flags = GetArgFlags(name);
if (!flags || !TypedArg(*flags)) return false;
if ((*flags & require) != require || (*flags & forbid) != 0) {
throw std::logic_error(
strprintf("Bug: Can't call %s on arg %s registered with flags 0x%08x (requires 0x%x, disallows 0x%x)",
context, name, *flags, require, forbid));
}
return true;
}
fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
{
if (IsArgNegated(arg)) return fs::path{};
@ -414,9 +472,10 @@ std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
{
bool typed_arg = CheckArgFlags(strArg, /*require=*/ ALLOW_STRING | ALLOW_LIST, /*forbid=*/ 0, __func__);
std::vector<std::string> result;
for (const common::SettingsValue& value : GetSettingsList(strArg)) {
result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
result.push_back(ConvertToString(value, typed_arg).value_or(""));
}
return result;
}
@ -514,22 +573,13 @@ std::string ArgsManager::GetArg(const std::string& strArg, const std::string& st
std::optional<std::string> ArgsManager::GetArg(const std::string& strArg) const
{
const common::SettingsValue value = GetSetting(strArg);
return SettingToString(value);
}
std::optional<std::string> SettingToString(const common::SettingsValue& value)
{
if (value.isNull()) return std::nullopt;
if (value.isFalse()) return "0";
if (value.isTrue()) return "1";
if (value.isNum()) return value.getValStr();
return value.get_str();
bool typed_arg = CheckArgFlags(strArg, /*require=*/ ALLOW_STRING, /*forbid=*/ ALLOW_LIST, __func__);
return ConvertToString(GetSetting(strArg), typed_arg);
}
std::string SettingToString(const common::SettingsValue& value, const std::string& strDefault)
{
return SettingToString(value).value_or(strDefault);
return ConvertToString(value, /*typed_arg=*/false).value_or(strDefault);
}
int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
@ -539,22 +589,13 @@ int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) cons
std::optional<int64_t> ArgsManager::GetIntArg(const std::string& strArg) const
{
const common::SettingsValue value = GetSetting(strArg);
return SettingToInt(value);
}
std::optional<int64_t> SettingToInt(const common::SettingsValue& value)
{
if (value.isNull()) return std::nullopt;
if (value.isFalse()) return 0;
if (value.isTrue()) return 1;
if (value.isNum()) return value.getInt<int64_t>();
return LocaleIndependentAtoi<int64_t>(value.get_str());
bool typed_arg = CheckArgFlags(strArg, /*require=*/ ALLOW_INT, /*forbid=*/ ALLOW_LIST, __func__);
return ConvertToInt(GetSetting(strArg), typed_arg);
}
int64_t SettingToInt(const common::SettingsValue& value, int64_t nDefault)
{
return SettingToInt(value).value_or(nDefault);
return ConvertToInt(value, /*typed_arg=*/false).value_or(nDefault);
}
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
@ -564,20 +605,13 @@ bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
std::optional<bool> ArgsManager::GetBoolArg(const std::string& strArg) const
{
const common::SettingsValue value = GetSetting(strArg);
return SettingToBool(value);
}
std::optional<bool> SettingToBool(const common::SettingsValue& value)
{
if (value.isNull()) return std::nullopt;
if (value.isBool()) return value.get_bool();
return InterpretBool(value.get_str());
bool typed_arg = CheckArgFlags(strArg, /*require=*/ 0, /*forbid=*/ ALLOW_LIST, __func__);
return ConvertToBool(GetSetting(strArg), typed_arg);
}
bool SettingToBool(const common::SettingsValue& value, bool fDefault)
{
return SettingToBool(value).value_or(fDefault);
return ConvertToBool(value, /*typed_arg=*/false).value_or(fDefault);
}
bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
@ -591,6 +625,7 @@ bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strVa
bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
{
LOCK(cs_args);
CheckArgFlags(strArg, /*require=*/ ALLOW_BOOL, /*forbid=*/ ALLOW_LIST, __func__);
if (IsArgSet(strArg)) return false;
m_settings.forced_settings[SettingName(strArg)] = fValue;
return true;
@ -599,6 +634,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
{
LOCK(cs_args);
CheckArgFlags(strArg, /*require=*/ ALLOW_STRING, /*forbid=*/ 0, __func__);
m_settings.forced_settings[SettingName(strArg)] = strValue;
}
@ -810,7 +846,7 @@ std::variant<ChainType, std::string> ArgsManager::GetChainArg() const
/* ignore_default_section_config= */ false,
/*ignore_nonpersistent=*/false,
/* get_chain_type= */ true);
return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
return ConvertToBool(value, /*typed_arg=*/false).value_or(false);
};
const bool fRegTest = get_net("-regtest");

View File

@ -85,13 +85,8 @@ struct SectionInfo {
};
std::string SettingToString(const common::SettingsValue&, const std::string&);
std::optional<std::string> SettingToString(const common::SettingsValue&);
int64_t SettingToInt(const common::SettingsValue&, int64_t);
std::optional<int64_t> SettingToInt(const common::SettingsValue&);
bool SettingToBool(const common::SettingsValue&, bool);
std::optional<bool> SettingToBool(const common::SettingsValue&);
class ArgsManager
{
@ -142,6 +137,8 @@ protected:
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
bool CheckArgFlags(const std::string& name, uint32_t require, uint32_t forbid, const char* context) const;
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
/**

View File

@ -135,12 +135,16 @@ public:
BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), "zzzzz");
} else if (expect.string_value) {
BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), expect.string_value);
} else if (success) {
BOOST_CHECK_THROW(test.GetArg("-value", "zzzzz"), std::logic_error);
}
if (expect.default_int) {
BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), 99999);
} else if (expect.int_value) {
BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), *expect.int_value);
} else if (success) {
BOOST_CHECK_THROW(test.GetIntArg("-value", 99999), std::logic_error);
}
if (expect.default_bool) {
@ -149,11 +153,16 @@ public:
} else if (expect.bool_value) {
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), *expect.bool_value);
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), *expect.bool_value);
} else if (success) {
BOOST_CHECK_THROW(test.GetBoolArg("-value", false), std::logic_error);
BOOST_CHECK_THROW(test.GetBoolArg("-value", true), std::logic_error);
}
if (expect.list_value) {
auto l = test.GetArgs("-value");
BOOST_CHECK_EQUAL_COLLECTIONS(l.begin(), l.end(), expect.list_value->begin(), expect.list_value->end());
} else if (success) {
BOOST_CHECK_THROW(test.GetArgs("-value"), std::logic_error);
}
}
};
@ -176,89 +185,89 @@ BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest)
CheckValue(M::ALLOW_ANY, "-value=2", Expect{"2"}.String("2").Int(2).Bool(true).List({"2"}));
CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"}));
CheckValue(M::ALLOW_BOOL, nullptr, Expect{{}});
CheckValue(M::ALLOW_BOOL, "-novalue", Expect{false});
CheckValue(M::ALLOW_BOOL, nullptr, Expect{{}}.DefaultBool());
CheckValue(M::ALLOW_BOOL, "-novalue", Expect{false}.Bool(false));
CheckValue(M::ALLOW_BOOL, "-novalue=", Expect{{}}.Error("Can not negate -value at the same time as setting value ''."));
CheckValue(M::ALLOW_BOOL, "-novalue=0", Expect{{}}.Error("Can not negate -value at the same time as setting value '0'."));
CheckValue(M::ALLOW_BOOL, "-novalue=1", Expect{false});
CheckValue(M::ALLOW_BOOL, "-novalue=1", Expect{false}.Bool(false));
CheckValue(M::ALLOW_BOOL, "-novalue=2", Expect{{}}.Error("Can not negate -value at the same time as setting value '2'."));
CheckValue(M::ALLOW_BOOL, "-novalue=abc", Expect{{}}.Error("Can not negate -value at the same time as setting value 'abc'."));
CheckValue(M::ALLOW_BOOL, "-value", Expect{true});
CheckValue(M::ALLOW_BOOL, "-value=", Expect{""});
CheckValue(M::ALLOW_BOOL, "-value=0", Expect{false});
CheckValue(M::ALLOW_BOOL, "-value=1", Expect{true});
CheckValue(M::ALLOW_BOOL, "-value", Expect{true}.Bool(true));
CheckValue(M::ALLOW_BOOL, "-value=", Expect{""}.DefaultBool());
CheckValue(M::ALLOW_BOOL, "-value=0", Expect{false}.Bool(false));
CheckValue(M::ALLOW_BOOL, "-value=1", Expect{true}.Bool(true));
CheckValue(M::ALLOW_BOOL, "-value=2", Expect{{}}.Error("Can not set -value value to '2'. It must be set to 0 or 1."));
CheckValue(M::ALLOW_BOOL, "-value=abc", Expect{{}}.Error("Can not set -value value to 'abc'. It must be set to 0 or 1."));
CheckValue(M::ALLOW_INT, nullptr, Expect{{}});
CheckValue(M::ALLOW_INT, "-novalue", Expect{false});
CheckValue(M::ALLOW_INT, nullptr, Expect{{}}.DefaultInt().DefaultBool());
CheckValue(M::ALLOW_INT, "-novalue", Expect{false}.Int(0).Bool(false));
CheckValue(M::ALLOW_INT, "-novalue=", Expect{{}}.Error("Can not negate -value at the same time as setting value ''."));
CheckValue(M::ALLOW_INT, "-novalue=0", Expect{{}}.Error("Can not negate -value at the same time as setting value '0'."));
CheckValue(M::ALLOW_INT, "-novalue=1", Expect{false});
CheckValue(M::ALLOW_INT, "-novalue=1", Expect{false}.Int(0).Bool(false));
CheckValue(M::ALLOW_INT, "-novalue=2", Expect{{}}.Error("Can not negate -value at the same time as setting value '2'."));
CheckValue(M::ALLOW_INT, "-novalue=abc", Expect{{}}.Error("Can not negate -value at the same time as setting value 'abc'."));
CheckValue(M::ALLOW_INT, "-value", Expect{{}}.Error("Can not set -value with no value. Please specify value with -value=value. It must be set to an integer."));
CheckValue(M::ALLOW_INT, "-value=", Expect{""});
CheckValue(M::ALLOW_INT, "-value=0", Expect{0});
CheckValue(M::ALLOW_INT, "-value=1", Expect{1});
CheckValue(M::ALLOW_INT, "-value=2", Expect{2});
CheckValue(M::ALLOW_INT, "-value=", Expect{""}.DefaultInt().DefaultBool());
CheckValue(M::ALLOW_INT, "-value=0", Expect{0}.Int(0).Bool(false));
CheckValue(M::ALLOW_INT, "-value=1", Expect{1}.Int(1).Bool(true));
CheckValue(M::ALLOW_INT, "-value=2", Expect{2}.Int(2).Bool(true));
CheckValue(M::ALLOW_INT, "-value=abc", Expect{{}}.Error("Can not set -value value to 'abc'. It must be set to an integer."));
CheckValue(M::ALLOW_STRING, nullptr, Expect{{}});
CheckValue(M::ALLOW_STRING, "-novalue", Expect{false});
CheckValue(M::ALLOW_STRING, nullptr, Expect{{}}.DefaultString().DefaultBool());
CheckValue(M::ALLOW_STRING, "-novalue", Expect{false}.String("").Bool(false));
CheckValue(M::ALLOW_STRING, "-novalue=", Expect{{}}.Error("Can not negate -value at the same time as setting value ''."));
CheckValue(M::ALLOW_STRING, "-novalue=0", Expect{{}}.Error("Can not negate -value at the same time as setting value '0'."));
CheckValue(M::ALLOW_STRING, "-novalue=1", Expect{false});
CheckValue(M::ALLOW_STRING, "-novalue=1", Expect{false}.String("").Bool(false));
CheckValue(M::ALLOW_STRING, "-novalue=2", Expect{{}}.Error("Can not negate -value at the same time as setting value '2'."));
CheckValue(M::ALLOW_STRING, "-novalue=abc", Expect{{}}.Error("Can not negate -value at the same time as setting value 'abc'."));
CheckValue(M::ALLOW_STRING, "-value", Expect{{}}.Error("Can not set -value with no value. Please specify value with -value=value. It must be set to a string."));
CheckValue(M::ALLOW_STRING, "-value=", Expect{""});
CheckValue(M::ALLOW_STRING, "-value=0", Expect{"0"});
CheckValue(M::ALLOW_STRING, "-value=1", Expect{"1"});
CheckValue(M::ALLOW_STRING, "-value=2", Expect{"2"});
CheckValue(M::ALLOW_STRING, "-value=abc", Expect{"abc"});
CheckValue(M::ALLOW_STRING, "-value=", Expect{""}.DefaultString().DefaultBool());
CheckValue(M::ALLOW_STRING, "-value=0", Expect{"0"}.String("0").DefaultBool());
CheckValue(M::ALLOW_STRING, "-value=1", Expect{"1"}.String("1").DefaultBool());
CheckValue(M::ALLOW_STRING, "-value=2", Expect{"2"}.String("2").DefaultBool());
CheckValue(M::ALLOW_STRING, "-value=abc", Expect{"abc"}.String("abc").DefaultBool());
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, nullptr, Expect{{}});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue", Expect{false});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, nullptr, Expect{{}}.DefaultInt().DefaultBool());
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue", Expect{false}.Int(0).Bool(false));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue=", Expect{{}}.Error("Can not negate -value at the same time as setting value ''."));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue=0", Expect{{}}.Error("Can not negate -value at the same time as setting value '0'."));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue=1", Expect{false});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue=1", Expect{false}.Int(0).Bool(false));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue=2", Expect{{}}.Error("Can not negate -value at the same time as setting value '2'."));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-novalue=abc", Expect{{}}.Error("Can not negate -value at the same time as setting value 'abc'."));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value", Expect{true});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=", Expect{""});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=0", Expect{0});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=1", Expect{1});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=2", Expect{2});
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value", Expect{true}.Int(1).Bool(true));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=", Expect{""}.DefaultInt().DefaultBool());
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=0", Expect{0}.Int(0).Bool(false));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=1", Expect{1}.Int(1).Bool(true));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=2", Expect{2}.Int(2).Bool(true));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=abc", Expect{{}}.Error("Can not set -value value to 'abc'. It must be set to an integer."));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, nullptr, Expect{{}});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue", Expect{false});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, nullptr, Expect{{}}.DefaultString().DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue", Expect{false}.String("").Bool(false));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue=", Expect{{}}.Error("Can not negate -value at the same time as setting value ''."));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue=0", Expect{{}}.Error("Can not negate -value at the same time as setting value '0'."));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue=1", Expect{false});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue=1", Expect{false}.String("").Bool(false));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue=2", Expect{{}}.Error("Can not negate -value at the same time as setting value '2'."));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-novalue=abc", Expect{{}}.Error("Can not negate -value at the same time as setting value 'abc'."));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value", Expect{true});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=", Expect{""});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=0", Expect{"0"});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=1", Expect{"1"});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=2", Expect{"2"});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=abc", Expect{"abc"});
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value", Expect{true}.DefaultString().Bool(true));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=", Expect{""}.DefaultString().DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=0", Expect{"0"}.String("0").DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=1", Expect{"1"}.String("1").DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=2", Expect{"2"}.String("2").DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=abc", Expect{"abc"}.String("abc").DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, nullptr, Expect{{}});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue", Expect{false});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, nullptr, Expect{{}}.List({}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue", Expect{false}.List({}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue=", Expect{{}}.Error("Can not negate -value at the same time as setting value ''."));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue=0", Expect{{}}.Error("Can not negate -value at the same time as setting value '0'."));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue=1", Expect{false});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue=1", Expect{false}.List({}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue=2", Expect{{}}.Error("Can not negate -value at the same time as setting value '2'."));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-novalue=abc", Expect{{}}.Error("Can not negate -value at the same time as setting value 'abc'."));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value", Expect{{}}.Error("Can not set -value with no value. Please specify value with -value=value. It must be set to a string."));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=", Expect{""});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=0", Expect{"0"});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=1", Expect{"1"});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=2", Expect{"2"});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=abc", Expect{"abc"});
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=", Expect{""}.List({""}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=0", Expect{"0"}.List({"0"}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=1", Expect{"1"}.List({"1"}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=2", Expect{"2"}.List({"2"}));
CheckValue(M::ALLOW_STRING | M::ALLOW_LIST, "-value=abc", Expect{"abc"}.List({"abc"}));
}
BOOST_FIXTURE_TEST_CASE(util_CheckBoolStringsNotSpecial, CheckValueTest)
@ -274,6 +283,18 @@ BOOST_FIXTURE_TEST_CASE(util_CheckBoolStringsNotSpecial, CheckValueTest)
using M = ArgsManager;
CheckValue(M::ALLOW_BOOL, "-value=true", Expect{{}}.Error("Can not set -value value to 'true'. It must be set to 0 or 1."));
CheckValue(M::ALLOW_BOOL, "-value=false", Expect{{}}.Error("Can not set -value value to 'false'. It must be set to 0 or 1."));
// Similarly, check "true" and "false" are not treated specially when
// ALLOW_BOOL is combined with ALLOW_INT and ALLOW_STRING. (The only
// difference ALLOW_BOOL makes for int and string arguments is that it
// enables "-foo" syntax with no equal sign assigning explicit int or string
// values. This is useful for arguments like "-upgradewallet" or "-listen"
// that primarily toggle features on and off, but also accept optional int
// or string values to influence behavior.)
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=true", Expect{{}}.Error("Can not set -value value to 'true'. It must be set to an integer."));
CheckValue(M::ALLOW_INT | M::ALLOW_BOOL, "-value=false", Expect{{}}.Error("Can not set -value value to 'false'. It must be set to an integer."));
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=true", Expect{"true"}.String("true").DefaultBool());
CheckValue(M::ALLOW_STRING | M::ALLOW_BOOL, "-value=false", Expect{"false"}.String("false").DefaultBool());
}
BOOST_AUTO_TEST_CASE(util_CheckSingleValue)

View File

@ -44,13 +44,21 @@ FUZZ_TARGET(system, .init = initialize_system)
args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16));
},
[&] {
args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
// Avoid Can't call SoftSetArg on arg registered with flags 0x8d8d8d00 (requires 0x2, disallows 0x10)
try {
args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
} catch (const std::logic_error&) {
}
},
[&] {
args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16));
},
[&] {
args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool());
// Avoid Can't call SoftSetBoolArg on arg registered with flags 0x8d8d8d00 (requires 0x2, disallows 0x10)
try {
args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool());
} catch (const std::logic_error&) {
}
},
[&] {
const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN});
@ -107,11 +115,23 @@ FUZZ_TARGET(system, .init = initialize_system)
const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
const bool b = fuzzed_data_provider.ConsumeBool();
(void)args_manager.GetIntArg(s1, i64);
(void)args_manager.GetArg(s1, s2);
try {
(void)args_manager.GetIntArg(s1, i64);
} catch (const std::logic_error&) {
}
try {
(void)args_manager.GetArg(s1, s2);
} catch (const std::logic_error&) {
}
(void)args_manager.GetArgFlags(s1);
(void)args_manager.GetArgs(s1);
(void)args_manager.GetBoolArg(s1, b);
try {
(void)args_manager.GetArgs(s1);
} catch (const std::logic_error&) {
}
try {
(void)args_manager.GetBoolArg(s1, b);
} catch (const std::logic_error&) {
}
try {
(void)args_manager.GetChainTypeString();
} catch (const std::runtime_error&) {