Commit 9dbfba7c authored by Dominik Charousset's avatar Dominik Charousset

Remove custom CLI parsing

- Consistently use the same parser on all `config_value` inputs.
- Ensure that CAF accepts JSON notation in `get_as` and `get_or`.
- Remove error-prone auto-unboxing for variants in the testing DSL.
parent 31be7eb2
...@@ -325,6 +325,13 @@ private: ...@@ -325,6 +325,13 @@ private:
/// Returns all user-provided configuration parameters. /// Returns all user-provided configuration parameters.
CAF_CORE_EXPORT const settings& content(const actor_system_config& cfg); CAF_CORE_EXPORT const settings& content(const actor_system_config& cfg);
/// Returns whether `xs` associates a value of type `T` to `name`.
/// @relates actor_system_config
template <class T>
bool holds_alternative(const actor_system_config& cfg, string_view name) {
return holds_alternative<T>(content(cfg), name);
}
/// Tries to retrieve the value associated to `name` from `cfg`. /// Tries to retrieve the value associated to `name` from `cfg`.
/// @relates actor_system_config /// @relates actor_system_config
template <class T> template <class T>
...@@ -339,29 +346,20 @@ T get(const actor_system_config& cfg, string_view name) { ...@@ -339,29 +346,20 @@ T get(const actor_system_config& cfg, string_view name) {
return get<T>(content(cfg), name); return get<T>(content(cfg), name);
} }
/// Retrieves the value associated to `name` from `cfg` or returns /// Retrieves the value associated to `name` from `cfg` or returns `fallback`.
/// `default_value`.
/// @relates actor_system_config /// @relates actor_system_config
template <class T, class = typename std::enable_if< template <class To = get_or_auto_deduce, class Fallback>
!std::is_pointer<T>::value auto get_or(const actor_system_config& cfg, string_view name,
&& !std::is_convertible<T, string_view>::value>::type> Fallback&& fallback) {
T get_or(const actor_system_config& cfg, string_view name, T default_value) { return get_or<To>(content(cfg), name, std::forward<Fallback>(fallback));
return get_or(content(cfg), name, std::move(default_value));
} }
/// Retrieves the value associated to `name` from `cfg` or returns /// Tries to retrieve the value associated to `name` from `cfg` as an instance
/// `default_value`. /// of type `T`.
/// @relates actor_system_config
inline std::string get_or(const actor_system_config& cfg, string_view name,
string_view default_value) {
return get_or(content(cfg), name, default_value);
}
/// Returns whether `xs` associates a value of type `T` to `name`.
/// @relates actor_system_config /// @relates actor_system_config
template <class T> template <class T>
bool holds_alternative(const actor_system_config& cfg, string_view name) { expected<T> get_as(const actor_system_config& cfg, string_view name) {
return holds_alternative<T>(content(cfg), name); return get_as<T>(content(cfg), name);
} }
} // namespace caf } // namespace caf
...@@ -48,7 +48,7 @@ public: ...@@ -48,7 +48,7 @@ public:
config_value (*get)(const void*); config_value (*get)(const void*);
/// Human-readable name of the option's type. /// Human-readable name of the option's type.
std::string type_name; string_view type_name;
}; };
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
......
This diff is collapsed.
...@@ -36,6 +36,8 @@ class CAF_CORE_EXPORT config_list_consumer { ...@@ -36,6 +36,8 @@ class CAF_CORE_EXPORT config_list_consumer {
public: public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
config_list_consumer() = default;
config_list_consumer(const config_option_set* options, config_list_consumer(const config_option_set* options,
config_consumer* parent); config_consumer* parent);
...@@ -60,18 +62,20 @@ public: ...@@ -60,18 +62,20 @@ public:
template <class T> template <class T>
void value(T&& x) { void value(T&& x) {
xs_.emplace_back(std::forward<T>(x)); result.emplace_back(std::forward<T>(x));
} }
config_value::list result;
std::string qualified_key(); std::string qualified_key();
private: private:
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
const config_option_set* options_ = nullptr; const config_option_set* options_ = nullptr;
variant<config_consumer*, config_list_consumer*, config_value_consumer*> variant<none_t, config_consumer*, config_list_consumer*,
config_value_consumer*>
parent_; parent_;
config_value::list xs_;
}; };
/// Consumes a series of key-value pairs from an application configuration. /// Consumes a series of key-value pairs from an application configuration.
......
...@@ -101,21 +101,25 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x); ...@@ -101,21 +101,25 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x);
// -- CAF types ---------------------------------------------------------------- // -- CAF types ----------------------------------------------------------------
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_address& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv4_address&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_subnet& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv4_subnet&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv4_endpoint& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv4_endpoint&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_address& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv6_address&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_subnet& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv6_subnet&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_endpoint& x); CAF_CORE_EXPORT void parse(string_parser_state&, ipv6_endpoint&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, uri& x); CAF_CORE_EXPORT void parse(string_parser_state&, uri&);
CAF_CORE_EXPORT void parse(string_parser_state& ps, config_value& x); CAF_CORE_EXPORT void parse(string_parser_state&, config_value&);
CAF_CORE_EXPORT void parse(string_parser_state&, std::vector<config_value>&);
CAF_CORE_EXPORT void parse(string_parser_state&, dictionary<config_value>&);
// -- variadic utility --------------------------------------------------------- // -- variadic utility ---------------------------------------------------------
...@@ -209,6 +213,7 @@ void parse(string_parser_state& ps, ...@@ -209,6 +213,7 @@ void parse(string_parser_state& ps,
x = value_type{since_epoch}; x = value_type{since_epoch};
} }
/*
// -- container types ---------------------------------------------------------- // -- container types ----------------------------------------------------------
CAF_CORE_EXPORT void parse_element(string_parser_state& ps, std::string& x, CAF_CORE_EXPORT void parse_element(string_parser_state& ps, std::string& x,
...@@ -304,6 +309,7 @@ void parse_element(string_parser_state& ps, std::pair<First, Second>& kvp, ...@@ -304,6 +309,7 @@ void parse_element(string_parser_state& ps, std::pair<First, Second>& kvp,
} }
parse_element(ps, kvp.second, char_blacklist); parse_element(ps, kvp.second, char_blacklist);
} }
*/
// -- convenience functions ---------------------------------------------------- // -- convenience functions ----------------------------------------------------
......
...@@ -102,6 +102,28 @@ void read_config_list(State& ps, Consumer&& consumer) { ...@@ -102,6 +102,28 @@ void read_config_list(State& ps, Consumer&& consumer) {
// clang-format on // clang-format on
} }
// Like read_config_list, but without surrounding '[]'.
template <class State, class Consumer>
void lift_config_list(State& ps, Consumer&& consumer) {
// clang-format off
start();
state(init) {
epsilon(before_value)
}
term_state(before_value) {
transition(before_value, " \t\n")
fsm_epsilon(read_config_comment(ps, consumer), before_value, '#')
fsm_epsilon(read_config_value(ps, consumer, std::true_type{}), after_value)
}
term_state(after_value) {
transition(after_value, " \t\n")
transition(before_value, ',')
fsm_epsilon(read_config_comment(ps, consumer), after_value, '#')
}
fin();
// clang-format on
}
template <bool Nested = true, class State, class Consumer> template <bool Nested = true, class State, class Consumer>
void read_config_map(State& ps, Consumer&& consumer) { void read_config_map(State& ps, Consumer&& consumer) {
std::string key; std::string key;
...@@ -140,6 +162,7 @@ void read_config_map(State& ps, Consumer&& consumer) { ...@@ -140,6 +162,7 @@ void read_config_map(State& ps, Consumer&& consumer) {
unstable_state(after_value) { unstable_state(after_value) {
transition(after_value, " \t") transition(after_value, " \t")
transition(had_newline, "\n") transition(had_newline, "\n")
transition_if(!Nested, after_comma, ',')
transition(await_key_name, ',') transition(await_key_name, ',')
transition_if(Nested, done, '}', consumer.end_map()) transition_if(Nested, done, '}', consumer.end_map())
fsm_epsilon(read_config_comment(ps, consumer), had_newline, '#') fsm_epsilon(read_config_comment(ps, consumer), had_newline, '#')
...@@ -157,6 +180,9 @@ void read_config_map(State& ps, Consumer&& consumer) { ...@@ -157,6 +180,9 @@ void read_config_map(State& ps, Consumer&& consumer) {
epsilon_if(!Nested, done) epsilon_if(!Nested, done)
epsilon(unexpected_end_of_input) epsilon(unexpected_end_of_input)
} }
term_state(after_comma) {
epsilon(await_key_name)
}
state(unexpected_end_of_input) { state(unexpected_end_of_input) {
// no transitions, only needed for the unstable states // no transitions, only needed for the unstable states
} }
......
...@@ -189,6 +189,13 @@ public: ...@@ -189,6 +189,13 @@ public:
return true; return true;
} else if constexpr (std::is_same<T, char>::value) { } else if constexpr (std::is_same<T, char>::value) {
return value(string_view{x, strlen(x)}); return value(string_view{x, strlen(x)});
} else if constexpr (std::is_same<T, void>::value) {
sep();
result_ += "*<";
auto addr = reinterpret_cast<intptr_t>(x);
result_ += std::to_string(addr);
result_ += '>';
return true;
} else { } else {
sep(); sep();
result_ += '*'; result_ += '*';
......
...@@ -56,9 +56,8 @@ config_value get_impl(const void* ptr) { ...@@ -56,9 +56,8 @@ config_value get_impl(const void* ptr) {
template <class T> template <class T>
config_option::meta_state* option_meta_state_instance() { config_option::meta_state* option_meta_state_instance() {
using trait = detail::config_value_access_t<T>;
static config_option::meta_state obj{sync_impl<T>, get_impl<T>, static config_option::meta_state obj{sync_impl<T>, get_impl<T>,
trait::type_name()}; config_value::mapped_type_name<T>()};
return &obj; return &obj;
} }
......
...@@ -58,6 +58,10 @@ struct parser_state { ...@@ -58,6 +58,10 @@ struct parser_state {
e = last; e = last;
} }
parser_state(const parser_state&) noexcept = default;
parser_state& operator=(const parser_state&) noexcept = default;
/// Returns the null terminator when reaching the end of the string, /// Returns the null terminator when reaching the end of the string,
/// otherwise the next character. /// otherwise the next character.
char next() noexcept { char next() noexcept {
......
...@@ -170,6 +170,8 @@ enum class sec : uint8_t { ...@@ -170,6 +170,8 @@ enum class sec : uint8_t {
/// An operation failed because the callee does not implement this /// An operation failed because the callee does not implement this
/// functionality. /// functionality.
unsupported_operation, unsupported_operation,
/// A key lookup failed.
no_such_key = 65,
}; };
/// @relates sec /// @relates sec
......
...@@ -38,8 +38,8 @@ CAF_CORE_EXPORT std::string to_string(const settings& xs); ...@@ -38,8 +38,8 @@ CAF_CORE_EXPORT std::string to_string(const settings& xs);
/// Tries to retrieve the value associated to `name` from `xs`. /// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value /// @relates config_value
CAF_CORE_EXPORT const config_value* CAF_CORE_EXPORT const config_value* get_if(const settings* xs,
get_if(const settings* xs, string_view name); string_view name);
/// Tries to retrieve the value associated to `name` from `xs`. /// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value /// @relates config_value
...@@ -54,12 +54,14 @@ auto get_if(const settings* xs, string_view name) { ...@@ -54,12 +54,14 @@ auto get_if(const settings* xs, string_view name) {
/// @relates config_value /// @relates config_value
template <class T> template <class T>
bool holds_alternative(const settings& xs, string_view name) { bool holds_alternative(const settings& xs, string_view name) {
using access = detail::config_value_access_t<T>;
if (auto value = get_if(&xs, name)) if (auto value = get_if(&xs, name))
return access::is(*value); return holds_alternative<T>(*value);
else
return false; return false;
} }
/// Retrieves the value associated to `name` from `xs`.
/// @relates actor_system_config
template <class T> template <class T>
T get(const settings& xs, string_view name) { T get(const settings& xs, string_view name) {
auto result = get_if<T>(&xs, name); auto result = get_if<T>(&xs, name);
...@@ -67,27 +69,39 @@ T get(const settings& xs, string_view name) { ...@@ -67,27 +69,39 @@ T get(const settings& xs, string_view name) {
return detail::move_if_not_ptr(result); return detail::move_if_not_ptr(result);
} }
template <class T, class = typename std::enable_if< /// Retrieves the value associated to `name` from `xs` or returns `fallback`.
!std::is_pointer<T>::value /// @relates actor_system_config
&& !std::is_convertible<T, string_view>::value>::type> template <class To = get_or_auto_deduce, class Fallback>
T get_or(const settings& xs, string_view name, T default_value) { auto get_or(const settings& xs, string_view name, Fallback&& fallback) {
auto result = get_if<T>(&xs, name); if (auto ptr = get_if(&xs, name)) {
if (result) return get_or<To>(*ptr, std::forward<Fallback>(fallback));
return std::move(*result); } else if constexpr (std::is_same<To, get_or_auto_deduce>::value) {
return default_value; using guide = get_or_deduction_guide<std::decay_t<Fallback>>;
return guide::convert(std::forward<Fallback>(fallback));
} else {
return To{std::forward<Fallback>(fallback)};
}
} }
CAF_CORE_EXPORT std::string /// Tries to retrieve the value associated to `name` from `xs` as an instance of
get_or(const settings& xs, string_view name, string_view default_value); /// type `T`.
/// @relates actor_system_config
template <class T>
expected<T> get_as(const settings& xs, string_view name) {
if (auto ptr = get_if(&xs, name))
return get_as<T>(*ptr);
else
return {sec::no_such_key};
}
/// @private /// @private
CAF_CORE_EXPORT config_value& CAF_CORE_EXPORT config_value& put_impl(settings& dict,
put_impl(settings& dict, const std::vector<string_view>& path, const std::vector<string_view>& path,
config_value& value); config_value& value);
/// @private /// @private
CAF_CORE_EXPORT config_value& CAF_CORE_EXPORT config_value& put_impl(settings& dict, string_view key,
put_impl(settings& dict, string_view key, config_value& value); config_value& value);
/// Converts `value` to a `config_value` and assigns it to `key`. /// Converts `value` to a `config_value` and assigns it to `key`.
/// @param dict Dictionary of key-value pairs. /// @param dict Dictionary of key-value pairs.
...@@ -118,7 +132,7 @@ CAF_CORE_EXPORT config_value::list& put_list(settings& xs, std::string name); ...@@ -118,7 +132,7 @@ CAF_CORE_EXPORT config_value::list& put_list(settings& xs, std::string name);
/// Inserts a new list named `name` into the dictionary `xs` and returns /// Inserts a new list named `name` into the dictionary `xs` and returns
/// a reference to it. Overrides existing entries with the same name. /// a reference to it. Overrides existing entries with the same name.
CAF_CORE_EXPORT config_value::dictionary& CAF_CORE_EXPORT config_value::dictionary& put_dictionary(settings& xs,
put_dictionary(settings& xs, std::string name); std::string name);
} // namespace caf } // namespace caf
...@@ -128,7 +128,7 @@ private: ...@@ -128,7 +128,7 @@ private:
return false; return false;
for (const auto& lbl : labels) { for (const auto& lbl : labels) {
if (auto ptr = get_if<settings>(cfg, lbl.str())) { if (auto ptr = get_if<settings>(cfg, lbl.str())) {
if (auto bounds = get_if<std::vector<value_type>>(ptr, "buckets")) { if (auto bounds = get_as<std::vector<value_type>>(*ptr, "buckets")) {
std::sort(bounds->begin(), bounds->end()); std::sort(bounds->begin(), bounds->end());
bounds->erase(std::unique(bounds->begin(), bounds->end()), bounds->erase(std::unique(bounds->begin(), bounds->end()),
bounds->end()); bounds->end());
......
...@@ -367,7 +367,7 @@ public: ...@@ -367,7 +367,7 @@ public:
if (auto grp = get_if<settings>(config_, prefix)) { if (auto grp = get_if<settings>(config_, prefix)) {
if (sub_settings = get_if<settings>(grp, name); if (sub_settings = get_if<settings>(grp, name);
sub_settings != nullptr) { sub_settings != nullptr) {
if (auto lst = get_if<upper_bounds_list>(sub_settings, "buckets")) { if (auto lst = get_as<upper_bounds_list>(*sub_settings, "buckets")) {
std::sort(lst->begin(), lst->end()); std::sort(lst->begin(), lst->end());
lst->erase(std::unique(lst->begin(), lst->end()), lst->end()); lst->erase(std::unique(lst->begin(), lst->end()), lst->end());
if (!lst->empty()) if (!lst->empty())
......
...@@ -295,10 +295,10 @@ actor_system::actor_system(actor_system_config& cfg) ...@@ -295,10 +295,10 @@ actor_system::actor_system(actor_system_config& cfg)
hook->init(*this); hook->init(*this);
// Cache some configuration parameters for faster lookups at runtime. // Cache some configuration parameters for faster lookups at runtime.
using string_list = std::vector<std::string>; using string_list = std::vector<std::string>;
if (auto lst = get_if<string_list>(&cfg, if (auto lst = get_as<string_list>(cfg,
"caf.metrics-filters.actors.includes")) "caf.metrics-filters.actors.includes"))
metrics_actors_includes_ = std::move(*lst); metrics_actors_includes_ = std::move(*lst);
if (auto lst = get_if<string_list>(&cfg, if (auto lst = get_as<string_list>(cfg,
"caf.metrics-filters.actors.excludes")) "caf.metrics-filters.actors.excludes"))
metrics_actors_excludes_ = std::move(*lst); metrics_actors_excludes_ = std::move(*lst);
if (!metrics_actors_includes_.empty()) if (!metrics_actors_includes_.empty())
......
...@@ -142,7 +142,7 @@ string_view config_option::type_name() const noexcept { ...@@ -142,7 +142,7 @@ string_view config_option::type_name() const noexcept {
} }
bool config_option::is_flag() const noexcept { bool config_option::is_flag() const noexcept {
return type_name() == "boolean"; return type_name() == "bool";
} }
bool config_option::has_flat_cli_name() const noexcept { bool config_option::has_flat_cli_name() const noexcept {
......
...@@ -62,6 +62,11 @@ auto no_conversions() { ...@@ -62,6 +62,11 @@ auto no_conversions() {
return detail::make_overload(no_conversion<To, From>()...); return detail::make_overload(no_conversion<To, From>()...);
} }
template <class T>
constexpr ptrdiff_t signed_index_of() {
return detail::tl_index_of<typename config_value::types, T>::value;
}
} // namespace } // namespace
// -- constructors, destructors, and assignment operators ---------------------- // -- constructors, destructors, and assignment operators ----------------------
...@@ -153,6 +158,10 @@ const char* config_value::type_name_at_index(size_t index) noexcept { ...@@ -153,6 +158,10 @@ const char* config_value::type_name_at_index(size_t index) noexcept {
return type_names[index]; return type_names[index];
} }
ptrdiff_t config_value::signed_index() const noexcept {
return static_cast<ptrdiff_t>(data_.index());
}
// -- utility ------------------------------------------------------------------ // -- utility ------------------------------------------------------------------
type_id_t config_value::type_id() const noexcept { type_id_t config_value::type_id() const noexcept {
...@@ -321,6 +330,16 @@ expected<timespan> config_value::to_timespan() const { ...@@ -321,6 +330,16 @@ expected<timespan> config_value::to_timespan() const {
return visit(f, data_); return visit(f, data_);
} }
expected<uri> config_value::to_uri() const {
using result_type = expected<uri>;
auto f = detail::make_overload(
no_conversions<uri, none_t, bool, integer, real, timespan,
config_value::list, config_value::dictionary>(),
[](const uri& x) { return result_type{x}; },
[](const std::string& x) { return make_uri(x); });
return visit(f, data_);
}
expected<config_value::list> config_value::to_list() const { expected<config_value::list> config_value::to_list() const {
using result_type = expected<list>; using result_type = expected<list>;
auto dict_to_list = [](const dictionary& dict, list& result) { auto dict_to_list = [](const dictionary& dict, list& result) {
...@@ -335,18 +354,17 @@ expected<config_value::list> config_value::to_list() const { ...@@ -335,18 +354,17 @@ expected<config_value::list> config_value::to_list() const {
auto f = detail::make_overload( auto f = detail::make_overload(
no_conversions<list, none_t, bool, integer, real, timespan, uri>(), no_conversions<list, none_t, bool, integer, real, timespan, uri>(),
[dict_to_list](const std::string& x) { [dict_to_list](const std::string& x) {
// Check whether we can parse the string as a list. If that fails, try // Check whether we can parse the string as a list. However, we also
// whether we can parse it as a dictionary instead (and then convert that // accept dictionaries that we convert to lists of key-value pairs. We
// to a list). // need to try converting to dictionary *first*, because detail::parse for
// the list otherwise produces a list with one dictionary.
if (config_value::dictionary dict; detail::parse(x, dict) == none) {
config_value::list tmp; config_value::list tmp;
if (detail::parse(x, tmp, detail::require_opening_char) == none)
return result_type{std::move(tmp)};
config_value::dictionary dict;
if (detail::parse(x, dict, detail::require_opening_char) == none) {
tmp.clear();
dict_to_list(dict, tmp); dict_to_list(dict, tmp);
return result_type{std::move(tmp)}; return result_type{std::move(tmp)};
} }
if (config_value::list tmp; detail::parse(x, tmp) == none)
return result_type{std::move(tmp)};
std::string msg = "cannot convert "; std::string msg = "cannot convert ";
detail::print_escaped(msg, x); detail::print_escaped(msg, x);
msg += " to a list"; msg += " to a list";
...@@ -383,12 +401,11 @@ expected<config_value::dictionary> config_value::to_dictionary() const { ...@@ -383,12 +401,11 @@ expected<config_value::dictionary> config_value::to_dictionary() const {
return result_type{std::move(err)}; return result_type{std::move(err)};
} }
}, },
[](const std::string& x) { [this](const std::string& x) {
if (dictionary tmp; detail::parse(x, tmp) == none) { if (dictionary tmp; detail::parse(x, tmp) == none)
return result_type{std::move(tmp)}; return result_type{std::move(tmp)};
} if (auto lst = to_list()) {
if (list tmp; detail::parse(x, tmp) == none) { config_value ls{std::move(*lst)};
config_value ls{std::move(tmp)};
if (auto res = ls.to_dictionary()) if (auto res = ls.to_dictionary())
return res; return res;
} }
...@@ -453,14 +470,66 @@ config_value::parse_msg_impl(string_view str, ...@@ -453,14 +470,66 @@ config_value::parse_msg_impl(string_view str,
// -- related free functions --------------------------------------------------- // -- related free functions ---------------------------------------------------
bool operator<(double x, const config_value& y) {
return config_value{x} < y;
}
bool operator<=(double x, const config_value& y) {
return config_value{x} <= y;
}
bool operator==(double x, const config_value& y) {
return config_value{x} == y;
}
bool operator>(double x, const config_value& y) {
return config_value{x} > y;
}
bool operator>=(double x, const config_value& y) {
return config_value{x} >= y;
}
bool operator<(const config_value& x, double y) {
return x < config_value{y};
}
bool operator<=(const config_value& x, double y) {
return x <= config_value{y};
}
bool operator==(const config_value& x, double y) {
return x == config_value{y};
}
bool operator>(const config_value& x, double y) {
return x > config_value{y};
}
bool operator>=(const config_value& x, double y) {
return x >= config_value{y};
}
bool operator<(const config_value& x, const config_value& y) { bool operator<(const config_value& x, const config_value& y) {
return x.get_data() < y.get_data(); return x.get_data() < y.get_data();
} }
bool operator<=(const config_value& x, const config_value& y) {
return x.get_data() <= y.get_data();
}
bool operator==(const config_value& x, const config_value& y) { bool operator==(const config_value& x, const config_value& y) {
return x.get_data() == y.get_data(); return x.get_data() == y.get_data();
} }
bool operator>(const config_value& x, const config_value& y) {
return x.get_data() > y.get_data();
}
bool operator>=(const config_value& x, const config_value& y) {
return x.get_data() >= y.get_data();
}
namespace { namespace {
void to_string_impl(std::string& str, const config_value& x); void to_string_impl(std::string& str, const config_value& x);
......
...@@ -428,7 +428,7 @@ bool pull(config_value_reader& reader, T& x) { ...@@ -428,7 +428,7 @@ bool pull(config_value_reader& reader, T& x) {
auto& top = reader.top(); auto& top = reader.top();
if (holds_alternative<const config_value*>(top)) { if (holds_alternative<const config_value*>(top)) {
auto ptr = get<const config_value*>(top); auto ptr = get<const config_value*>(top);
if (auto val = get_if<internal_type>(ptr)) { if (auto val = get_as<internal_type>(*ptr)) {
assign(*val); assign(*val);
reader.pop(); reader.pop();
return true; return true;
...@@ -446,7 +446,7 @@ bool pull(config_value_reader& reader, T& x) { ...@@ -446,7 +446,7 @@ bool pull(config_value_reader& reader, T& x) {
return false; return false;
} }
auto ptr = std::addressof(seq.current()); auto ptr = std::addressof(seq.current());
if (auto val = get_if<internal_type>(ptr)) { if (auto val = get_as<internal_type>(*ptr)) {
assign(*val); assign(*val);
seq.advance(); seq.advance();
return true; return true;
......
...@@ -268,11 +268,12 @@ bool config_value_writer::begin_associative_array(size_t) { ...@@ -268,11 +268,12 @@ bool config_value_writer::begin_associative_array(size_t) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
settings* inner = nullptr; settings* inner = nullptr;
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* val) { [this, &inner](config_value* val) {
// Morph the top element into a dictionary. // Morph the top element into a dictionary.
auto& dict = val->as_dictionary(); auto& dict = val->as_dictionary();
dict.clear(); dict.clear();
st_.top() = &dict; st_.top() = &dict;
inner = &dict;
return true; return true;
}, },
[this](settings*) { [this](settings*) {
......
...@@ -43,12 +43,12 @@ config_list_consumer::config_list_consumer(config_value_consumer* parent) ...@@ -43,12 +43,12 @@ config_list_consumer::config_list_consumer(config_value_consumer* parent)
} }
pec config_list_consumer::end_list() { pec config_list_consumer::end_list() {
auto f = make_overload( auto f = make_overload([](none_t) { return pec::success; },
[this](config_consumer* ptr) { [this](config_consumer* ptr) {
return ptr->value(config_value{std::move(xs_)}); return ptr->value(config_value{std::move(result)});
}, },
[this](auto* ptr) { [this](auto* ptr) {
ptr->value(config_value{std::move(xs_)}); ptr->value(config_value{std::move(result)});
return pec::success; return pec::success;
}); });
return visit(f, parent_); return visit(f, parent_);
...@@ -59,7 +59,8 @@ config_consumer config_list_consumer::begin_map() { ...@@ -59,7 +59,8 @@ config_consumer config_list_consumer::begin_map() {
} }
std::string config_list_consumer::qualified_key() { std::string config_list_consumer::qualified_key() {
auto f = make_overload([](config_value_consumer*) { return std::string{}; }, auto f = make_overload([](none_t) { return std::string{}; },
[](config_value_consumer*) { return std::string{}; },
[](auto* ptr) { return ptr->qualified_key(); }); [](auto* ptr) { return ptr->qualified_key(); });
return visit(f, parent_); return visit(f, parent_);
} }
......
...@@ -165,17 +165,62 @@ void parse(string_parser_state& ps, uri& x) { ...@@ -165,17 +165,62 @@ void parse(string_parser_state& ps, uri& x) {
void parse(string_parser_state& ps, config_value& x) { void parse(string_parser_state& ps, config_value& x) {
ps.skip_whitespaces(); ps.skip_whitespaces();
if (ps.at_end()) { if (!ps.at_end()) {
ps.code = pec::unexpected_eof;
return;
}
// Safe the string as fallback.
string_view str{ps.i, ps.e};
// Dispatch to parser.
detail::config_value_consumer f; detail::config_value_consumer f;
parser::read_config_value(ps, f); parser::read_config_value(ps, f);
if (ps.code <= pec::trailing_character) if (ps.code <= pec::trailing_character)
x = std::move(f.result); x = std::move(f.result);
} else {
ps.code = pec::unexpected_eof;
}
}
void parse(string_parser_state& ps, std::vector<config_value>& x) {
ps.skip_whitespaces();
if (!ps.at_end()) {
detail::config_list_consumer f;
auto fallback = ps;
if (ps.consume('[')) {
parser::read_config_list(ps, f);
if (ps.code == pec::success) {
x = std::move(f.result);
} else {
// Rewind parser state and try parsing again as a list without
// surrounding '[]' characters. This catches edge cases like
// '[1, 2], [3, 4]'. On error, we restore the parser state after the
// first error.
auto first_failure = ps;
ps = fallback;
f.result.clear();
parser::lift_config_list(ps, f);
if (ps.code <= pec::trailing_character)
x = std::move(f.result);
else
ps = first_failure;
}
} else {
// If the string isn't surrounded by '[]' in the first place, we call
// lift_config_list and keep it's result in any case.
parser::lift_config_list(ps, f);
if (ps.code == pec::success)
x = std::move(f.result);
}
} else {
ps.code = pec::unexpected_eof;
}
}
void parse(string_parser_state& ps, dictionary<config_value>& x) {
ps.skip_whitespaces();
if (!ps.at_end()) {
detail::config_consumer f{x};
if (ps.consume('{'))
parser::read_config_map(ps, f);
else
parser::read_config_map<false>(ps, f);
} else {
ps.code = pec::unexpected_eof;
}
} }
PARSE_IMPL(ipv4_address, ipv4_address) PARSE_IMPL(ipv4_address, ipv4_address)
......
...@@ -310,7 +310,7 @@ void logger::init(actor_system_config& cfg) { ...@@ -310,7 +310,7 @@ void logger::init(actor_system_config& cfg) {
return CAF_LOG_LEVEL_QUIET; return CAF_LOG_LEVEL_QUIET;
}; };
auto read_filter = [&cfg](string_list& var, string_view key) { auto read_filter = [&cfg](string_list& var, string_view key) {
if (auto lst = get_if<string_list>(&cfg, key)) if (auto lst = get_as<string_list>(cfg, key))
var = std::move(*lst); var = std::move(*lst);
}; };
cfg_.file_verbosity = get_verbosity("caf.logger.file.verbosity"); cfg_.file_verbosity = get_verbosity("caf.logger.file.verbosity");
......
...@@ -44,8 +44,7 @@ config_value bool_get_neg(const void* ptr) { ...@@ -44,8 +44,7 @@ config_value bool_get_neg(const void* ptr) {
return config_value{!*static_cast<const bool*>(ptr)}; return config_value{!*static_cast<const bool*>(ptr)};
} }
meta_state bool_neg_meta{bool_sync_neg, bool_get_neg, meta_state bool_neg_meta{bool_sync_neg, bool_get_neg, "bool"};
detail::config_value_access_t<bool>::type_name()};
template <uint64_t Denominator> template <uint64_t Denominator>
error sync_timespan(void* ptr, config_value& x) { error sync_timespan(void* ptr, config_value& x) {
...@@ -67,11 +66,10 @@ config_value get_timespan(const void* ptr) { ...@@ -67,11 +66,10 @@ config_value get_timespan(const void* ptr) {
return config_value{val}; return config_value{val};
} }
meta_state us_res_meta{sync_timespan<1000>, get_timespan<1000>, meta_state us_res_meta{sync_timespan<1000>, get_timespan<1000>, "timespan"};
detail::config_value_access_t<timespan>::type_name()};
meta_state ms_res_meta{sync_timespan<1000000>, get_timespan<1000000>, meta_state ms_res_meta{sync_timespan<1000000>, get_timespan<1000000>,
detail::config_value_access_t<timespan>::type_name()}; "timespan"};
} // namespace } // namespace
......
...@@ -249,10 +249,8 @@ void abstract_coordinator::init(actor_system_config& cfg) { ...@@ -249,10 +249,8 @@ void abstract_coordinator::init(actor_system_config& cfg) {
namespace sr = defaults::scheduler; namespace sr = defaults::scheduler;
max_throughput_ = get_or(cfg, "caf.scheduler.max-throughput", max_throughput_ = get_or(cfg, "caf.scheduler.max-throughput",
sr::max_throughput); sr::max_throughput);
if (auto num_workers = get_if<size_t>(&cfg, "caf.scheduler.max-threads")) num_workers_ = get_or(cfg, "caf.scheduler.max-threads",
num_workers_ = *num_workers; default_thread_count());
else
num_workers_ = default_thread_count();
} }
actor_system::module::id_t abstract_coordinator::id() const { actor_system::module::id_t abstract_coordinator::id() const {
......
...@@ -43,12 +43,12 @@ const config_value* get_if(const settings* xs, string_view name) { ...@@ -43,12 +43,12 @@ const config_value* get_if(const settings* xs, string_view name) {
name.substr(pos + 1)); name.substr(pos + 1));
} }
std::string get_or(const settings& xs, string_view name, expected<std::string> get_or(const settings& xs, string_view name,
string_view default_value) { const char* fallback) {
auto result = get_if<std::string>(&xs, name); if (auto ptr = get_if(&xs, name))
if (result) return get_as<std::string>(*ptr);
return std::move(*result); else
return std::string{default_value.begin(), default_value.end()}; return {std::string{fallback}};
} }
config_value& put_impl(settings& dict, const std::vector<string_view>& path, config_value& put_impl(settings& dict, const std::vector<string_view>& path,
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <vector> #include <vector>
using namespace caf; using namespace caf;
using namespace caf::literals;
using namespace std::string_literals; using namespace std::string_literals;
...@@ -161,12 +162,19 @@ CAF_TEST(file input overrides defaults but CLI args always win) { ...@@ -161,12 +162,19 @@ CAF_TEST(file input overrides defaults but CLI args always win) {
#define CHECK_SYNCED(var, value) \ #define CHECK_SYNCED(var, value) \
do { \ do { \
CAF_CHECK_EQUAL(var, value); \ CAF_CHECK_EQUAL(var, value); \
CAF_CHECK_EQUAL(get<decltype(var)>(cfg, #var), value); \ if (auto maybe_val = get_as<decltype(var)>(cfg, #var)) { \
CAF_CHECK_EQUAL(*maybe_val, value); \
} else { \
auto cv = get_if(std::addressof(cfg.content), #var); \
CAF_ERROR("expected type " \
<< config_value::mapped_type_name<decltype(var)>() \
<< ", got: " << cv->type_name()); \
} \
} while (false) } while (false)
// Checks whether an entry in content(cfg) is equal to `value`. // Checks whether an entry in content(cfg) is equal to `value`.
#define CHECK_TEXT_ONLY(type, var, value) \ #define CHECK_TEXT_ONLY(type, var, value) \
CAF_CHECK_EQUAL(get<type>(cfg, #var), value) CAF_CHECK_EQUAL(get_as<type>(cfg, #var), value)
#define ADD(var) add(var, #var, "...") #define ADD(var) add(var, #var, "...")
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "core-test.hpp" #include "core-test.hpp"
#include <sstream>
#include "caf/make_config_option.hpp" #include "caf/make_config_option.hpp"
#include "caf/config_value.hpp" #include "caf/config_value.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
...@@ -32,6 +34,148 @@ using std::string; ...@@ -32,6 +34,148 @@ using std::string;
namespace { namespace {
struct state;
struct baseline {
std::vector<std::string> cli;
std::string conf;
settings res;
std::function<bool(const state&)> predicate;
};
struct request_pair {
my_request first;
my_request second;
};
template <class Inspector>
bool inspect(Inspector& f, request_pair& x) {
return f.object(x).fields(f.field("first", x.first),
f.field("second", x.second));
}
struct state {
s1 my_app_s1;
std::vector<int32_t> my_app_vector;
level my_app_severity = level::trace;
my_request my_app_request;
request_pair my_app_request_pair;
config_option_set options;
state() {
config_option_adder{options, "?my.app"}
.add(my_app_s1, "s1", "")
.add(my_app_vector, "vector,v", "")
.add(my_app_severity, "severity,s", "")
.add(my_app_request, "request,r", "")
.add(my_app_request_pair, "request-pair,R", "");
config_option_adder{options, "sys"}
.add<std::string>("path,p", "")
.add<int8_t>("threads,tTd", "");
}
void run(baseline& x, size_t index) {
settings res;
std::istringstream src{x.conf};
if (auto parsed = actor_system_config::parse_config(src, options)) {
res = std::move(*parsed);
} else {
CAF_ERROR("failed to parse baseline at index " << index << ": "
<< parsed.error());
return;
}
auto [code, pos] = options.parse(res, x.cli);
if (pos != x.cli.end()) {
CAF_ERROR("failed to parse all arguments for baseline at index "
<< index << ", stopped at: " << *pos << " (" << code << ')');
return;
}
if (code != pec::success) {
CAF_ERROR("CLI arguments for baseline at index "
<< index << " failed to parse: " << code);
return;
}
if (!x.predicate(*this)) {
CAF_ERROR("predicate for baseline at index " << index << "failed! ");
return;
}
MESSAGE("all checks for baseline at index " << index << " passed");
}
};
struct fixture {
std::vector<baseline> baselines;
template <class Predicate>
void add_test(std::vector<std::string> cli, std::string conf, settings res,
Predicate f) {
baselines.emplace_back(baseline{
std::move(cli),
std::move(conf),
std::move(res),
f,
});
}
template <class Predicate>
void add_test(std::vector<std::string> cli, std::string conf, std::string res,
Predicate f) {
config_value cv_res{res};
if (auto parsed = get_as<settings>(cv_res))
add_test(std::move(cli), std::move(conf), std::move(*parsed),
std::move(f));
else
CAF_FAIL("failed to parse result settings: " << parsed.error()
<< "\nINPUT:\n"
<< res << '\n');
}
template <class Res>
void add_test(std::vector<std::string> cli, std::string conf, Res&& res) {
return add_test(std::move(cli), std::move(conf), std::forward<Res>(res),
[](const state&) { return true; });
}
fixture() {
using ivec = std::vector<int32_t>;
add_test({"-s", "error"}, "", R"_(my { app { severity = "error" } })_",
[](auto& st) {
return CHECK_EQ(st.my_app_severity, level::error);
});
add_test({"-v", "1, 2, 3"}, "", R"_(my { app { vector = [1, 2, 3] } })_",
[](auto& st) {
return CHECK_EQ(st.my_app_vector, ivec({1, 2, 3}));
});
add_test({"-v", "[1, 2, 3]"}, "", R"_(my { app { vector = [1, 2, 3] } })_");
add_test({"-v[1, 2, 3]"}, "", R"_(my { app { vector = [1, 2, 3] } })_");
add_test({"-v1, 2, 3,"}, "", R"_(my { app { vector = [1, 2, 3] } })_");
add_test({"-r", R"_({"a":1,"b":2})_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({"-r", R"_(a=1,b=2)_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({R"_(--my.app.request={a=1,b=2})_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({R"_(--my.app.request=a=1,b=2,)_"}, "",
R"_(my { app { request { a = 1, b = 2 } } })_");
add_test({"-R",
R"_({"first": {"a": 1, "b": 2}, "second": {"a": 3, "b": 4}})_"},
"",
R"_(my { app { request-pair { first { a = 1, b = 2 },
second { a = 3, b = 4 } } } })_");
}
};
} // namespace
BEGIN_FIXTURE_SCOPE(fixture)
SCENARIO("options on the CLI override config files that override defaults") {
for (size_t index = 0; index < baselines.size(); ++index) {
state st;
st.run(baselines[index], index);
}
}
constexpr string_view category = "category"; constexpr string_view category = "category";
constexpr string_view name = "name"; constexpr string_view name = "name";
constexpr string_view explanation = "explanation"; constexpr string_view explanation = "explanation";
...@@ -97,8 +241,6 @@ void compare(const config_option& lhs, const config_option& rhs) { ...@@ -97,8 +241,6 @@ void compare(const config_option& lhs, const config_option& rhs) {
CAF_CHECK_EQUAL(lhs.full_name(), rhs.full_name()); CAF_CHECK_EQUAL(lhs.full_name(), rhs.full_name());
} }
} // namespace
CAF_TEST(copy constructor) { CAF_TEST(copy constructor) {
auto one = make_config_option<int>("cat1", "one", "option 1"); auto one = make_config_option<int>("cat1", "one", "option 1");
auto two = one; auto two = one;
...@@ -170,7 +312,7 @@ CAF_TEST(type double) { ...@@ -170,7 +312,7 @@ CAF_TEST(type double) {
CAF_TEST(type string) { CAF_TEST(type string) {
CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo"); CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "foo"); CAF_CHECK_EQUAL(unbox(read<string>(R"_("foo")_")), R"_("foo")_");
} }
CAF_TEST(type timespan) { CAF_TEST(type timespan) {
...@@ -230,3 +372,5 @@ CAF_TEST(find by long opt) { ...@@ -230,3 +372,5 @@ CAF_TEST(find by long opt) {
// No options to look through. // No options to look through.
check({}, false, false); check({}, false, false);
} }
END_FIXTURE_SCOPE()
...@@ -53,10 +53,11 @@ struct fixture { ...@@ -53,10 +53,11 @@ struct fixture {
settings cfg; settings cfg;
auto res = opts.parse(cfg, std::move(args)); auto res = opts.parse(cfg, std::move(args));
if (res.first != pec::success) if (res.first != pec::success)
return res.first; return {res.first};
if (auto x = get_if<T>(&cfg, key)) else if (auto x = get_as<T>(cfg, key))
return detail::move_if_not_ptr(x); return {std::move(*x)};
return sec::invalid_argument; else
return {sec::invalid_argument};
} }
std::string key = "value"; std::string key = "value";
...@@ -101,7 +102,7 @@ CAF_TEST(parse with ref syncing) { ...@@ -101,7 +102,7 @@ CAF_TEST(parse with ref syncing) {
settings cfg; settings cfg;
vector<string> args{"-i42", vector<string> args{"-i42",
"-f", "-f",
"1e12", "1e2",
"-shello", "-shello",
"--bar.l=[\"hello\", \"world\"]", "--bar.l=[\"hello\", \"world\"]",
"-d", "-d",
...@@ -114,34 +115,20 @@ CAF_TEST(parse with ref syncing) { ...@@ -114,34 +115,20 @@ CAF_TEST(parse with ref syncing) {
CAF_FAIL("parser stopped at: " << *res.second); CAF_FAIL("parser stopped at: " << *res.second);
CAF_MESSAGE("verify referenced values"); CAF_MESSAGE("verify referenced values");
CAF_CHECK_EQUAL(foo_i, 42); CAF_CHECK_EQUAL(foo_i, 42);
CAF_CHECK_EQUAL(foo_f, 1e12); CAF_CHECK_EQUAL(foo_f, 1e2);
CAF_CHECK_EQUAL(foo_b, true); CAF_CHECK_EQUAL(foo_b, true);
CAF_CHECK_EQUAL(bar_s, "hello"); CAF_CHECK_EQUAL(bar_s, "hello");
CAF_CHECK_EQUAL(bar_l, ls({"hello", "world"})); CAF_CHECK_EQUAL(bar_l, ls({"hello", "world"}));
CAF_CHECK_EQUAL(bar_d, ds({{"a", "a"}, {"b", "b"}})); CAF_CHECK_EQUAL(bar_d, ds({{"a", "a"}, {"b", "b"}}));
CAF_MESSAGE("verify dictionary content"); CAF_MESSAGE("verify dictionary content");
CAF_CHECK_EQUAL(get<int>(cfg, "foo.i"), 42); CAF_CHECK_EQUAL(get_as<int>(cfg, "foo.i"), 42);
} }
CAF_TEST(string parameters) { CAF_TEST(string parameters) {
opts.add<std::string>("value,v", "some value"); opts.add<std::string>("value,v", "some value");
CAF_MESSAGE("test string option with and without quotes");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"foo\\tbar\""}), "foo\tbar");
CAF_CHECK_EQUAL(read<std::string>({"--value=foobar"}), "foobar"); CAF_CHECK_EQUAL(read<std::string>({"--value=foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"foobar\""}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v", "foobar"}), "foobar"); CAF_CHECK_EQUAL(read<std::string>({"-v", "foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-v\"foobar\""}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"-vfoobar"}), "foobar"); CAF_CHECK_EQUAL(read<std::string>({"-vfoobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"'abc'\""}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"--value='abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"'abc'\""}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v", "'abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"-v'abc'"}), "'abc'");
CAF_CHECK_EQUAL(read<std::string>({"--value=\"123\""}), "123");
CAF_CHECK_EQUAL(read<std::string>({"--value=123"}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v", "\"123\""}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v", "123"}), "123");
CAF_CHECK_EQUAL(read<std::string>({"-v123"}), "123");
} }
CAF_TEST(flat CLI options) { CAF_TEST(flat CLI options) {
...@@ -202,18 +189,19 @@ CAF_TEST(CLI arguments override defaults) { ...@@ -202,18 +189,19 @@ CAF_TEST(CLI arguments override defaults) {
CAF_MESSAGE("test integer lists"); CAF_MESSAGE("test integer lists");
ints = int_list{1, 2, 3}; ints = int_list{1, 2, 3};
cfg["bar"] = config_value{ints}; cfg["bar"] = config_value{ints};
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({1, 2, 3})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none); CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none);
CAF_CHECK_EQUAL(ints, int_list({10, 20, 30})); CAF_CHECK_EQUAL(ints, int_list({10, 20, 30}));
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({10, 20, 30})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({10, 20, 30}));
CAF_MESSAGE("test string lists"); CAF_MESSAGE("test string lists");
strings = string_list{"one", "two", "three"}; strings = string_list{"one", "two", "three"};
cfg["foo"] = config_value{strings}; cfg["foo"] = config_value{strings};
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"one", "two", "three"})); string_list({"one", "two", "three"}));
CAF_CHECK_EQUAL(read<string_list>(cfg, {"--foo=[hello, world]"}), none); CAF_CHECK_EQUAL(read<string_list>(cfg, {R"_(--foo=["hello", "world"])_"}),
none);
CAF_CHECK_EQUAL(strings, string_list({"hello", "world"})); CAF_CHECK_EQUAL(strings, string_list({"hello", "world"}));
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"hello", "world"})); string_list({"hello", "world"}));
} }
SUBTEST("without ref syncing") { SUBTEST("without ref syncing") {
...@@ -223,15 +211,16 @@ CAF_TEST(CLI arguments override defaults) { ...@@ -223,15 +211,16 @@ CAF_TEST(CLI arguments override defaults) {
opts.add<int_list>("global", "bar,b", "some list"); opts.add<int_list>("global", "bar,b", "some list");
CAF_MESSAGE("test integer lists"); CAF_MESSAGE("test integer lists");
cfg["bar"] = config_value{int_list{1, 2, 3}}; cfg["bar"] = config_value{int_list{1, 2, 3}};
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({1, 2, 3})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none); CAF_CHECK_EQUAL(read<int_list>(cfg, {"--bar=[10, 20, 30]"}), none);
CAF_CHECK_EQUAL(get<int_list>(cfg, "bar"), int_list({10, 20, 30})); CAF_CHECK_EQUAL(get_as<int_list>(cfg, "bar"), int_list({10, 20, 30}));
CAF_MESSAGE("test string lists"); CAF_MESSAGE("test string lists");
cfg["foo"] = config_value{string_list{"one", "two", "three"}}; cfg["foo"] = config_value{string_list{"one", "two", "three"}};
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"one", "two", "three"})); string_list({"one", "two", "three"}));
CAF_CHECK_EQUAL(read<string_list>(cfg, {"--foo=[hello, world]"}), none); CAF_CHECK_EQUAL(read<string_list>(cfg, {R"_(--foo=["hello", "world"])_"}),
CAF_CHECK_EQUAL(get<string_list>(cfg, "foo"), none);
CAF_CHECK_EQUAL(get_as<string_list>(cfg, "foo"),
string_list({"hello", "world"})); string_list({"hello", "world"}));
} }
} }
...@@ -240,8 +229,7 @@ CAF_TEST(CLI arguments may use custom types) { ...@@ -240,8 +229,7 @@ CAF_TEST(CLI arguments may use custom types) {
settings cfg; settings cfg;
opts.add<foobar>("global", "foobar,f", "test option"); opts.add<foobar>("global", "foobar,f", "test option");
CAF_CHECK_EQUAL(read<foobar>(cfg, {"-f{foo=\"hello\",bar=\"world\"}"}), none); CAF_CHECK_EQUAL(read<foobar>(cfg, {"-f{foo=\"hello\",bar=\"world\"}"}), none);
auto fb = get_if<foobar>(&cfg, "foobar"); if (auto fb = get_as<foobar>(cfg, "foobar"); CAF_CHECK(fb))
if (CAF_CHECK(fb))
CAF_CHECK_EQUAL(*fb, foobar("hello", "world")); CAF_CHECK_EQUAL(*fb, foobar("hello", "world"));
} }
......
This diff is collapsed.
...@@ -42,6 +42,7 @@ using i64_list = std::vector<i64>; ...@@ -42,6 +42,7 @@ using i64_list = std::vector<i64>;
struct fixture { struct fixture {
config_value x; config_value x;
settings dummy;
template <class T> template <class T>
void set(const T& value) { void set(const T& value) {
...@@ -50,19 +51,11 @@ struct fixture { ...@@ -50,19 +51,11 @@ struct fixture {
CAF_FAIL("failed two write to settings: " << writer.get_error()); CAF_FAIL("failed two write to settings: " << writer.get_error());
} }
template <class T> const settings& xs() const {
optional<T> get(const settings& cfg, string_view key) { if (auto* ptr = get_if<settings>(&x))
if (auto ptr = get_if<T>(&cfg, key))
return *ptr; return *ptr;
return none;
}
template <class T>
optional<T> get(string_view key) {
if (auto* xs = get_if<settings>(&x))
return get<T>(*xs, key);
else else
CAF_FAIL("fixture does not contain a dictionary"); return dummy;
} }
}; };
...@@ -72,18 +65,18 @@ CAF_TEST_FIXTURE_SCOPE(config_value_writer_tests, fixture) ...@@ -72,18 +65,18 @@ CAF_TEST_FIXTURE_SCOPE(config_value_writer_tests, fixture)
CAF_TEST(structs become dictionaries) { CAF_TEST(structs become dictionaries) {
set(foobar{"hello", "world"}); set(foobar{"hello", "world"});
CAF_CHECK_EQUAL(get<std::string>("foo"), "hello"s); CAF_CHECK_EQUAL(get_as<std::string>(xs(), "foo"), "hello"s);
CAF_CHECK_EQUAL(get<std::string>("bar"), "world"s); CAF_CHECK_EQUAL(get_as<std::string>(xs(), "bar"), "world"s);
} }
CAF_TEST(nested structs become nested dictionaries) { CAF_TEST(nested structs become nested dictionaries) {
set(line{{10, 20, 30}, {70, 60, 50}}); set(line{{10, 20, 30}, {70, 60, 50}});
CAF_CHECK_EQUAL(get<i64>("p1.x"), 10_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p1.x"), 10_i64);
CAF_CHECK_EQUAL(get<i64>("p1.y"), 20_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p1.y"), 20_i64);
CAF_CHECK_EQUAL(get<i64>("p1.z"), 30_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p1.z"), 30_i64);
CAF_CHECK_EQUAL(get<i64>("p2.x"), 70_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p2.x"), 70_i64);
CAF_CHECK_EQUAL(get<i64>("p2.y"), 60_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p2.y"), 60_i64);
CAF_CHECK_EQUAL(get<i64>("p2.z"), 50_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "p2.z"), 50_i64);
} }
CAF_TEST(empty types and maps become dictionaries) { CAF_TEST(empty types and maps become dictionaries) {
...@@ -100,36 +93,36 @@ CAF_TEST(empty types and maps become dictionaries) { ...@@ -100,36 +93,36 @@ CAF_TEST(empty types and maps become dictionaries) {
tst.v7["two"] = 2; tst.v7["two"] = 2;
tst.v7["three"] = 3; tst.v7["three"] = 3;
set(tst); set(tst);
CAF_CHECK_EQUAL(get<settings>("v1"), settings{}); CAF_CHECK_EQUAL(get_as<settings>(xs(), "v1"), settings{});
CAF_CHECK_EQUAL(get<i64>("v2"), 42_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v2"), 42_i64);
CAF_CHECK_EQUAL(get<i64_list>("v3"), i64_list({-1, -2, -3, -4})); CAF_CHECK_EQUAL(get_as<i64_list>(xs(), "v3"), i64_list({-1, -2, -3, -4}));
if (auto v4 = get<config_value::list>("v4"); if (auto v4 = get_as<config_value::list>(xs(), "v4");
CAF_CHECK_EQUAL(v4->size(), 2u)) { CAF_CHECK(v4 && v4->size() == 2u)) {
if (auto v1 = v4->front(); CAF_CHECK(holds_alternative<settings>(v1))) { if (auto v1 = v4->front(); CAF_CHECK(holds_alternative<settings>(v1))) {
auto& v1_xs = caf::get<settings>(v1); auto& v1_xs = get<settings>(v1);
CAF_CHECK_EQUAL(get<double>(v1_xs, "content"), 0.0); CAF_CHECK_EQUAL(get<double>(v1_xs, "content"), 0.0);
CAF_CHECK_EQUAL(get<std::string>(v1_xs, "@content-type"), CAF_CHECK_EQUAL(get<std::string>(v1_xs, "@content-type"),
to_string(type_name_v<double>)); to_string(type_name_v<double>));
} }
if (auto v2 = v4->back(); CAF_CHECK(holds_alternative<settings>(v2))) { if (auto v2 = v4->back(); CAF_CHECK(holds_alternative<settings>(v2))) {
auto& v2_xs = caf::get<settings>(v2); auto& v2_xs = get<settings>(v2);
CAF_CHECK_EQUAL(get<double>(v2_xs, "content"), 1.0); CAF_CHECK_EQUAL(get<double>(v2_xs, "content"), 1.0);
CAF_CHECK_EQUAL(get<std::string>(v2_xs, "@content-type"), CAF_CHECK_EQUAL(get<std::string>(v2_xs, "@content-type"),
to_string(type_name_v<double>)); to_string(type_name_v<double>));
} }
} }
CAF_CHECK_EQUAL(get<i64_list>("v5"), i64_list({10, 20})); CAF_CHECK_EQUAL(get_as<i64_list>(xs(), "v5"), i64_list({10, 20}));
// TODO: check v6 // TODO: check v6
CAF_CHECK_EQUAL(get<i64>("v7.one"), 1_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v7.one"), 1_i64);
CAF_CHECK_EQUAL(get<i64>("v7.two"), 2_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v7.two"), 2_i64);
CAF_CHECK_EQUAL(get<i64>("v7.three"), 3_i64); CAF_CHECK_EQUAL(get_as<i64>(xs(), "v7.three"), 3_i64);
CAF_CHECK_EQUAL(get<config_value::list>("v8"), config_value::list()); CAF_CHECK_EQUAL(get_as<config_value::list>(xs(), "v8"), config_value::list());
} }
CAF_TEST(custom inspect overloads may produce single values) { CAF_TEST(custom inspect overloads may produce single values) {
auto tue = weekday::tuesday; auto tue = weekday::tuesday;
set(tue); set(tue);
CAF_CHECK_EQUAL(x, "tuesday"s); CAF_CHECK_EQUAL(get_as<std::string>(x), "tuesday"s);
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -4,6 +4,51 @@ ...@@ -4,6 +4,51 @@
#include "core-test.hpp" #include "core-test.hpp"
std::string to_string(level lvl) {
switch (lvl) {
case level::all:
return "all";
case level::trace:
return "trace";
case level::debug:
return "debug";
case level::warning:
return "warning";
case level::error:
return "error";
default:
return "???";
}
}
bool from_string(caf::string_view str, level& lvl) {
auto set = [&](level value) {
lvl = value;
return true;
};
if (str == "all")
return set(level::all);
else if (str == "trace")
return set(level::trace);
else if (str == "debug")
return set(level::debug);
else if (str == "warning")
return set(level::warning);
else if (str == "error")
return set(level::error);
else
return false;
}
bool from_integer(uint8_t val, level& lvl) {
if (val < 5) {
lvl = static_cast<level>(val);
return true;
} else {
return false;
}
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
using namespace caf; using namespace caf;
init_global_meta_objects<id_block::core_test>(); init_global_meta_objects<id_block::core_test>();
......
...@@ -150,7 +150,7 @@ struct s1 { ...@@ -150,7 +150,7 @@ struct s1 {
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, s1& x) { bool inspect(Inspector& f, s1& x) {
return f.object(x).fields(f.field("value", x.value)); return f.apply(x.value);
} }
struct s2 { struct s2 {
...@@ -159,7 +159,7 @@ struct s2 { ...@@ -159,7 +159,7 @@ struct s2 {
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, s2& x) { bool inspect(Inspector& f, s2& x) {
return f.object(x).fields(f.field("value", x.value)); return f.apply(x.value);
} }
struct s3 { struct s3 {
...@@ -171,7 +171,7 @@ struct s3 { ...@@ -171,7 +171,7 @@ struct s3 {
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, s3& x) { bool inspect(Inspector& f, s3& x) {
return f.object(x).fields(f.field("value", x.value)); return f.apply(x.value);
} }
struct test_array { struct test_array {
...@@ -267,21 +267,17 @@ bool inspect(Inspector& f, dummy_enum_class& x) { ...@@ -267,21 +267,17 @@ bool inspect(Inspector& f, dummy_enum_class& x) {
return f.apply(get, set); return f.apply(get, set);
} }
enum class level { all, trace, debug, warning, error }; enum class level : uint8_t { all, trace, debug, warning, error };
std::string to_string(level);
bool from_string(caf::string_view, level&);
bool from_integer(uint8_t, level&);
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, level& x) { bool inspect(Inspector& f, level& x) {
using integer_type = std::underlying_type_t<level>; return caf::default_enum_inspect(f, x);
auto get = [&x] { return static_cast<integer_type>(x); };
auto set = [&x](integer_type val) {
if (val >= 0 && val <= 4) {
x = static_cast<level>(val);
return true;
} else {
return false;
}
};
return f.apply(get, set);
} }
enum dummy_enum { de_foo, de_bar }; enum dummy_enum { de_foo, de_bar };
......
...@@ -82,12 +82,11 @@ CAF_TEST(config_consumer) { ...@@ -82,12 +82,11 @@ CAF_TEST(config_consumer) {
detail::parser::read_config(res, consumer); detail::parser::read_config(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success); CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(string_view(res.i, res.e), string_view()); CAF_CHECK_EQUAL(string_view(res.i, res.e), string_view());
CAF_CHECK_EQUAL(get<bool>(config, "is_server"), true); CAF_CHECK_EQUAL(get_as<bool>(config, "is_server"), true);
CAF_CHECK_EQUAL(get<uint16_t>(config, "port"), 4242u); CAF_CHECK_EQUAL(get_as<uint16_t>(config, "port"), 4242u);
CAF_CHECK_EQUAL(get<ls>(config, "nodes"), ls({"sun", "venus"})); CAF_CHECK_EQUAL(get_as<ls>(config, "nodes"), ls({"sun", "venus"}));
CAF_CHECK_EQUAL(get<string>(config, "logger.file-name"), "foobar.conf"); CAF_CHECK_EQUAL(get_as<string>(config, "logger.file-name"), "foobar.conf");
CAF_MESSAGE(config); CAF_CHECK_EQUAL(get_as<timespan>(config, "scheduler.timing"), timespan(2000));
CAF_CHECK_EQUAL(get<timespan>(config, "scheduler.timing"), timespan(2000));
} }
CAF_TEST(simplified syntax) { CAF_TEST(simplified syntax) {
......
...@@ -171,41 +171,14 @@ CAF_TEST(strings) { ...@@ -171,41 +171,14 @@ CAF_TEST(strings) {
CAF_CHECK_EQUAL(read<std::string>(" \" foo\t\" "), " foo\t"); CAF_CHECK_EQUAL(read<std::string>(" \" foo\t\" "), " foo\t");
} }
CAF_TEST(lists) {
using int_list = std::vector<int>;
using string_list = std::vector<std::string>;
CAF_CHECK_EQUAL(read<int_list>("1"), int_list({1}));
CAF_CHECK_EQUAL(read<int_list>("1, 2, 3"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<int_list>("[1, 2, 3]"), int_list({1, 2, 3}));
CAF_CHECK_EQUAL(read<string_list>("a, b , \" c \""),
string_list({"a", "b", " c "}));
}
CAF_TEST(maps) {
using int_map = std::map<std::string, int>;
CAF_CHECK_EQUAL(read<int_map>(R"(a=1, "b" = 42)"),
int_map({{"a", 1}, {"b", 42}}));
CAF_CHECK_EQUAL(read<int_map>(R"({ a = 1 , b = 42 ,} )"),
int_map({{"a", 1}, {"b", 42}}));
}
CAF_TEST(uris) { CAF_TEST(uris) {
using uri_list = std::vector<uri>; if (auto x_res = read<uri>("foo:bar")) {
auto x_res = read<uri>("foo:bar");
if (x_res == none) {
CAF_ERROR("my:path not recognized as URI");
return;
}
auto x = *x_res; auto x = *x_res;
CAF_CHECK_EQUAL(x.scheme(), "foo"); CAF_CHECK_EQUAL(x.scheme(), "foo");
CAF_CHECK_EQUAL(x.path(), "bar"); CAF_CHECK_EQUAL(x.path(), "bar");
auto ls = unbox(read<uri_list>("foo:bar, <http://actor-framework.org/doc>")); } else {
CAF_REQUIRE_EQUAL(ls.size(), 2u); CAF_ERROR("my:path not recognized as URI");
CAF_CHECK_EQUAL(ls[0].scheme(), "foo"); }
CAF_CHECK_EQUAL(ls[0].path(), "bar");
CAF_CHECK_EQUAL(ls[1].scheme(), "http");
CAF_CHECK_EQUAL(ls[1].authority().host, std::string{"actor-framework.org"});
CAF_CHECK_EQUAL(ls[1].path(), "doc");
} }
CAF_TEST(IPv4 address) { CAF_TEST(IPv4 address) {
......
...@@ -48,6 +48,7 @@ struct bool_parser { ...@@ -48,6 +48,7 @@ struct bool_parser {
detail::parser::read_bool(res, f); detail::parser::read_bool(res, f);
if (res.code == pec::success) if (res.code == pec::success)
return f.x; return f.x;
else
return res.code; return res.code;
} }
}; };
...@@ -61,23 +62,23 @@ struct fixture { ...@@ -61,23 +62,23 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(read_bool_tests, fixture) CAF_TEST_FIXTURE_SCOPE(read_bool_tests, fixture)
CAF_TEST(valid booleans) { CAF_TEST(valid booleans) {
CAF_CHECK_EQUAL(p("true"), true); CAF_CHECK_EQUAL(p("true"), res_t{true});
CAF_CHECK_EQUAL(p("false"), false); CAF_CHECK_EQUAL(p("false"), res_t{false});
} }
CAF_TEST(invalid booleans) { CAF_TEST(invalid booleans) {
CAF_CHECK_EQUAL(p(""), pec::unexpected_eof); CAF_CHECK_EQUAL(p(""), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("t"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("t"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("tr"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("tr"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("tru"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("tru"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p(" true"), pec::unexpected_character); CAF_CHECK_EQUAL(p(" true"), res_t{pec::unexpected_character});
CAF_CHECK_EQUAL(p("f"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("f"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("fa"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("fa"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("fal"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("fal"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p("fals"), pec::unexpected_eof); CAF_CHECK_EQUAL(p("fals"), res_t{pec::unexpected_eof});
CAF_CHECK_EQUAL(p(" false"), pec::unexpected_character); CAF_CHECK_EQUAL(p(" false"), res_t{pec::unexpected_character});
CAF_CHECK_EQUAL(p("tr\nue"), pec::unexpected_newline); CAF_CHECK_EQUAL(p("tr\nue"), res_t{pec::unexpected_newline});
CAF_CHECK_EQUAL(p("trues"), pec::trailing_character); CAF_CHECK_EQUAL(p("trues"), res_t{pec::trailing_character});
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -409,9 +409,10 @@ end object)_"); ...@@ -409,9 +409,10 @@ end object)_");
CAF_TEST(load inspectors support variant fields with fallbacks) { CAF_TEST(load inspectors support variant fields with fallbacks) {
fallback_dummy_message d; fallback_dummy_message d;
using content_type = decltype(d.content);
d.content = std::string{"hello world"}; d.content = std::string{"hello world"};
CAF_CHECK(inspect(f, d)); CAF_CHECK(inspect(f, d));
CAF_CHECK_EQUAL(d.content, 42.0); CAF_CHECK_EQUAL(d.content, content_type{42.0});
CAF_CHECK_EQUAL(f.log, R"_( CAF_CHECK_EQUAL(f.log, R"_(
begin object fallback_dummy_message begin object fallback_dummy_message
begin optional variant field content begin optional variant field content
......
...@@ -120,12 +120,12 @@ CAF_TEST(to_string converts messages to strings) { ...@@ -120,12 +120,12 @@ CAF_TEST(to_string converts messages to strings) {
CAF_CHECK_EQUAL(msg_as_string(R"__(this is a "test")__"), teststr); CAF_CHECK_EQUAL(msg_as_string(R"__(this is a "test")__"), teststr);
CAF_CHECK_EQUAL(msg_as_string(make_tuple(1, 2, 3), 4, 5), CAF_CHECK_EQUAL(msg_as_string(make_tuple(1, 2, 3), 4, 5),
"message([1, 2, 3], 4, 5)"); "message([1, 2, 3], 4, 5)");
CAF_CHECK_EQUAL(msg_as_string(s1{}), "message(s1([10, 20, 30]))"); CAF_CHECK_EQUAL(msg_as_string(s1{}), "message([10, 20, 30])");
s2 tmp; s2 tmp;
tmp.value[0][1] = 100; tmp.value[0][1] = 100;
CAF_CHECK_EQUAL(msg_as_string(s2{}), CAF_CHECK_EQUAL(msg_as_string(s2{}),
"message(s2([[1, 10], [2, 20], [3, 30], [4, 40]]))"); "message([[1, 10], [2, 20], [3, 30], [4, 40]])");
CAF_CHECK_EQUAL(msg_as_string(s3{}), "message(s3([1, 2, 3, 4]))"); CAF_CHECK_EQUAL(msg_as_string(s3{}), "message([1, 2, 3, 4])");
} }
CAF_TEST(match_elements exposes element types) { CAF_TEST(match_elements exposes element types) {
......
...@@ -120,7 +120,8 @@ CAF_TEST(message_lifetime_in_scoped_actor) { ...@@ -120,7 +120,8 @@ CAF_TEST(message_lifetime_in_scoped_actor) {
self->send(self, msg); self->send(self, msg);
CAF_CHECK_EQUAL(msg.cdata().get_reference_count(), 2u); CAF_CHECK_EQUAL(msg.cdata().get_reference_count(), 2u);
self->receive([&](int& value) { self->receive([&](int& value) {
CAF_CHECK_NOT_EQUAL(&value, msg.cdata().at(0)); auto addr = static_cast<void*>(&value);
CAF_CHECK_NOT_EQUAL(addr, msg.cdata().at(0));
value = 10; value = 10;
}); });
CAF_CHECK_EQUAL(msg.get_as<int>(0), 42); CAF_CHECK_EQUAL(msg.get_as<int>(0), 42);
......
...@@ -93,7 +93,7 @@ CAF_TEST(requests without result) { ...@@ -93,7 +93,7 @@ CAF_TEST(requests without result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((void), from(server).to(client)); expect((void), from(server).to(client));
CAF_CHECK_EQUAL(*result, unit); CAF_CHECK_EQUAL(*result, result_type{unit});
} }
SUBTEST("request.await") { SUBTEST("request.await") {
auto client = sys.spawn([=](event_based_actor* self) { auto client = sys.spawn([=](event_based_actor* self) {
...@@ -102,13 +102,13 @@ CAF_TEST(requests without result) { ...@@ -102,13 +102,13 @@ CAF_TEST(requests without result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((void), from(server).to(client)); expect((void), from(server).to(client));
CAF_CHECK_EQUAL(*result, unit); CAF_CHECK_EQUAL(*result, result_type{unit});
} }
SUBTEST("request.receive") { SUBTEST("request.receive") {
auto res_hdl = self->request(server, infinite, 1, 2); auto res_hdl = self->request(server, infinite, 1, 2);
run(); run();
res_hdl.receive([&] { *result = unit; }, ERROR_HANDLER); res_hdl.receive([&] { *result = unit; }, ERROR_HANDLER);
CAF_CHECK_EQUAL(*result, unit); CAF_CHECK_EQUAL(*result, result_type{unit});
} }
} }
...@@ -121,7 +121,7 @@ CAF_TEST(requests with integer result) { ...@@ -121,7 +121,7 @@ CAF_TEST(requests with integer result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((int), from(server).to(client).with(3)); expect((int), from(server).to(client).with(3));
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
SUBTEST("request.await") { SUBTEST("request.await") {
auto client = sys.spawn([=](event_based_actor* self) { auto client = sys.spawn([=](event_based_actor* self) {
...@@ -130,13 +130,13 @@ CAF_TEST(requests with integer result) { ...@@ -130,13 +130,13 @@ CAF_TEST(requests with integer result) {
run_once(); run_once();
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((int), from(server).to(client).with(3)); expect((int), from(server).to(client).with(3));
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
SUBTEST("request.receive") { SUBTEST("request.receive") {
auto res_hdl = self->request(server, infinite, 1, 2); auto res_hdl = self->request(server, infinite, 1, 2);
run(); run();
res_hdl.receive([&](int x) { *result = x; }, ERROR_HANDLER); res_hdl.receive([&](int x) { *result = x; }, ERROR_HANDLER);
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
} }
...@@ -150,7 +150,7 @@ CAF_TEST(delegated request with integer result) { ...@@ -150,7 +150,7 @@ CAF_TEST(delegated request with integer result) {
expect((int, int), from(client).to(server).with(1, 2)); expect((int, int), from(client).to(server).with(1, 2));
expect((int, int), from(client).to(worker).with(1, 2)); expect((int, int), from(client).to(worker).with(1, 2));
expect((int), from(worker).to(client).with(3)); expect((int), from(worker).to(client).with(3));
CAF_CHECK_EQUAL(*result, 3); CAF_CHECK_EQUAL(*result, result_type{3});
} }
CAF_TEST(requesters support fan_out_request) { CAF_TEST(requesters support fan_out_request) {
......
...@@ -61,23 +61,22 @@ struct fixture { ...@@ -61,23 +61,22 @@ struct fixture {
} }
}; };
const config_value& unpack(const settings& x, string_view key) { config_value unpack(const settings& x, string_view key) {
auto i = x.find(key); if (auto i = x.find(key); i != x.end())
if (i == x.end())
CAF_FAIL("key not found in dictionary: " << key);
return i->second; return i->second;
else
return {};
} }
template <class... Ts> template <class... Ts>
const config_value& unpack(const settings& x, string_view key, config_value
const char* next_key, Ts... keys) { unpack(const settings& x, string_view key, const char* next_key, Ts... keys) {
auto i = x.find(key); if (auto i = x.find(key); i == x.end())
if (i == x.end()) return {};
CAF_FAIL("key not found in dictionary: " << key); else if (auto ptr = get_if<settings>(std::addressof(i->second)))
if (!holds_alternative<settings>(i->second)) return unpack(*ptr, {next_key, strlen(next_key)}, keys...);
CAF_FAIL("value is not a dictionary: " << key); else
return unpack(get<settings>(i->second), {next_key, strlen(next_key)}, return {};
keys...);
} }
struct foobar { struct foobar {
...@@ -85,45 +84,12 @@ struct foobar { ...@@ -85,45 +84,12 @@ struct foobar {
int bar = 0; int bar = 0;
}; };
} // namespace template <class Inspector>
bool inspect(Inspector& f, foobar& x) {
namespace caf { return f.object(x).fields(f.field("foo", x.foo), f.field("bar", x.bar));
}
// Enable users to configure foobar's like this:
// my-value {
// foo = 42
// bar = 23
// }
template <>
struct config_value_access<foobar> {
static bool is(const config_value& x) {
auto dict = caf::get_if<config_value::dictionary>(&x);
if (dict != nullptr) {
return caf::get_if<int>(dict, "foo") != none
&& caf::get_if<int>(dict, "bar") != none;
}
return false;
}
static optional<foobar> get_if(const config_value* x) {
foobar result;
if (!is(*x))
return none;
const auto& dict = caf::get<config_value::dictionary>(*x);
result.foo = caf::get<int>(dict, "foo");
result.bar = caf::get<int>(dict, "bar");
return result;
}
static foobar get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
};
} // namespace caf } // namespace
CAF_TEST_FIXTURE_SCOPE(settings_tests, fixture) CAF_TEST_FIXTURE_SCOPE(settings_tests, fixture)
...@@ -135,11 +101,11 @@ CAF_TEST(put) { ...@@ -135,11 +101,11 @@ CAF_TEST(put) {
CAF_CHECK(x.contains("foo")); CAF_CHECK(x.contains("foo"));
CAF_CHECK(x.contains("logger")); CAF_CHECK(x.contains("logger"));
CAF_CHECK(x.contains("one")); CAF_CHECK(x.contains("one"));
CAF_CHECK_EQUAL(unpack(x, "foo"), "bar"s); CAF_CHECK_EQUAL(unpack(x, "foo"), config_value{"bar"s});
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), "four"s); CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), config_value{"four"s});
put(x, "logger.console", "trace"); put(x, "logger.console", "trace");
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "trace"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"trace"s});
} }
CAF_TEST(put missing) { CAF_TEST(put missing) {
...@@ -150,11 +116,11 @@ CAF_TEST(put missing) { ...@@ -150,11 +116,11 @@ CAF_TEST(put missing) {
CAF_CHECK(x.contains("foo")); CAF_CHECK(x.contains("foo"));
CAF_CHECK(x.contains("logger")); CAF_CHECK(x.contains("logger"));
CAF_CHECK(x.contains("one")); CAF_CHECK(x.contains("one"));
CAF_CHECK_EQUAL(unpack(x, "foo"), "bar"s); CAF_CHECK_EQUAL(unpack(x, "foo"), config_value{"bar"s});
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), "four"s); CAF_CHECK_EQUAL(unpack(x, "one", "two", "three"), config_value{"four"s});
put_missing(x, "logger.console", "trace"); put_missing(x, "logger.console", "trace");
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
} }
CAF_TEST(put list) { CAF_TEST(put list) {
...@@ -170,11 +136,12 @@ CAF_TEST(put list) { ...@@ -170,11 +136,12 @@ CAF_TEST(put list) {
CAF_TEST(put dictionary) { CAF_TEST(put dictionary) {
put_dictionary(x, "logger").emplace("console", "none"); put_dictionary(x, "logger").emplace("console", "none");
CAF_CHECK(x.contains("logger")); CAF_CHECK(x.contains("logger"));
CAF_CHECK_EQUAL(unpack(x, "logger", "console"), "none"s); CAF_CHECK_EQUAL(unpack(x, "logger", "console"), config_value{"none"s});
put_dictionary(x, "foo.bar").emplace("value", 42); put_dictionary(x, "foo.bar").emplace("value", 42);
CAF_CHECK_EQUAL(unpack(x, "foo", "bar", "value"), 42); CAF_CHECK_EQUAL(unpack(x, "foo", "bar", "value"), config_value{42});
put_dictionary(x, "one.two.three").emplace("four", "five"); put_dictionary(x, "one.two.three").emplace("four", "five");
CAF_CHECK_EQUAL(unpack(x, "one", "two", "three", "four"), "five"s); CAF_CHECK_EQUAL(unpack(x, "one", "two", "three", "four"),
config_value{"five"s});
} }
CAF_TEST(get and get_if) { CAF_TEST(get and get_if) {
...@@ -186,8 +153,8 @@ CAF_TEST(get and get_if) { ...@@ -186,8 +153,8 @@ CAF_TEST(get and get_if) {
CAF_CHECK(get<std::string>(x, "logger.console") == "none"s); CAF_CHECK(get<std::string>(x, "logger.console") == "none"s);
CAF_CHECK(get_if(&x, "one.two.three") != nullptr); CAF_CHECK(get_if(&x, "one.two.three") != nullptr);
CAF_CHECK(get_if<std::string>(&x, "one.two.three") == nullptr); CAF_CHECK(get_if<std::string>(&x, "one.two.three") == nullptr);
CAF_REQUIRE(get_if<int>(&x, "one.two.three") != none); if (CAF_CHECK(get_if<int64_t>(&x, "one.two.three") != nullptr))
CAF_CHECK(get<int>(x, "one.two.three") == 4); CAF_CHECK(get<int64_t>(x, "one.two.three") == 4);
} }
CAF_TEST(get_or) { CAF_TEST(get_or) {
...@@ -199,11 +166,10 @@ CAF_TEST(get_or) { ...@@ -199,11 +166,10 @@ CAF_TEST(get_or) {
CAF_TEST(custom type) { CAF_TEST(custom type) {
put(x, "my-value.foo", 42); put(x, "my-value.foo", 42);
put(x, "my-value.bar", 24); put(x, "my-value.bar", 24);
CAF_REQUIRE(holds_alternative<foobar>(x, "my-value")); if (auto fb = get_as<foobar>(x, "my-value"); CAF_CHECK(fb)) {
CAF_REQUIRE(get_if<foobar>(&x, "my-value") != caf::none); CAF_CHECK_EQUAL(fb->foo, 42);
auto fb = get<foobar>(x, "my-value"); CAF_CHECK_EQUAL(fb->bar, 24);
CAF_CHECK_EQUAL(fb.foo, 42); }
CAF_CHECK_EQUAL(fb.bar, 24);
} }
CAF_TEST(read_config accepts the to_string output of settings) { CAF_TEST(read_config accepts the to_string output of settings) {
......
...@@ -86,10 +86,10 @@ CAF_TEST(subspans) { ...@@ -86,10 +86,10 @@ CAF_TEST(subspans) {
CAF_TEST(free iterator functions) { CAF_TEST(free iterator functions) {
auto xs = make_span(chars); auto xs = make_span(chars);
CAF_CHECK_EQUAL(xs.begin(), begin(xs)); CAF_CHECK(xs.begin() == begin(xs));
CAF_CHECK_EQUAL(xs.cbegin(), cbegin(xs)); CAF_CHECK(xs.cbegin() == cbegin(xs));
CAF_CHECK_EQUAL(xs.end(), end(xs)); CAF_CHECK(xs.end() == end(xs));
CAF_CHECK_EQUAL(xs.cend(), cend(xs)); CAF_CHECK(xs.cend() == cend(xs));
} }
CAF_TEST(as bytes) { CAF_TEST(as bytes) {
...@@ -108,10 +108,10 @@ CAF_TEST(make_span) { ...@@ -108,10 +108,10 @@ CAF_TEST(make_span) {
CAF_CHECK(std::equal(xs.begin(), xs.end(), chars.begin())); CAF_CHECK(std::equal(xs.begin(), xs.end(), chars.begin()));
CAF_CHECK(std::equal(ys.begin(), ys.end(), chars.begin())); CAF_CHECK(std::equal(ys.begin(), ys.end(), chars.begin()));
CAF_CHECK(std::equal(zs.begin(), zs.end(), chars.begin())); CAF_CHECK(std::equal(zs.begin(), zs.end(), chars.begin()));
CAF_CHECK_EQUAL(end(xs), end(ys)); CAF_CHECK(end(xs) == end(ys));
CAF_CHECK_EQUAL(end(ys), end(zs)); CAF_CHECK(end(ys) == end(zs));
CAF_CHECK_EQUAL(begin(xs), begin(ys)); CAF_CHECK(begin(xs) == begin(ys));
CAF_CHECK_EQUAL(begin(ys), begin(zs)); CAF_CHECK(begin(ys) == begin(zs));
} }
CAF_TEST(spans are convertible from compatible containers) { CAF_TEST(spans are convertible from compatible containers) {
......
...@@ -81,8 +81,7 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10, ...@@ -81,8 +81,7 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
do { \ do { \
using type = std::decay_t<decltype(y)>; \ using type = std::decay_t<decltype(y)>; \
auto&& tmp = x; \ auto&& tmp = x; \
CAF_CHECK(holds_alternative<type>(tmp)); \ if (CAF_CHECK(holds_alternative<type>(tmp))) \
if (holds_alternative<type>(tmp)) \
CAF_CHECK_EQUAL(get<type>(tmp), y); \ CAF_CHECK_EQUAL(get<type>(tmp), y); \
} while (false) } while (false)
...@@ -102,9 +101,6 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10, ...@@ -102,9 +101,6 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
CAF_TEST(copying_moving_roundtrips) { CAF_TEST(copying_moving_roundtrips) {
actor_system_config cfg; actor_system_config cfg;
actor_system sys{cfg}; actor_system sys{cfg};
// default construction
variant<none_t> x1;
CAF_CHECK_EQUAL(x1, none);
variant<int, none_t> x2; variant<int, none_t> x2;
VARIANT_EQ(x2, 0); VARIANT_EQ(x2, 0);
v20 x3; v20 x3;
...@@ -129,11 +125,10 @@ CAF_TEST(constructors) { ...@@ -129,11 +125,10 @@ CAF_TEST(constructors) {
variant<float, int, std::string> b{"bar"s}; variant<float, int, std::string> b{"bar"s};
variant<int, std::string, double> c{123}; variant<int, std::string, double> c{123};
variant<bool, uint8_t> d{uint8_t{252}}; variant<bool, uint8_t> d{uint8_t{252}};
CAF_CHECK_EQUAL(a, 42); VARIANT_EQ(a, 42);
CAF_CHECK_EQUAL(b, "bar"s); VARIANT_EQ(b, "bar"s);
CAF_CHECK_EQUAL(c, 123); VARIANT_EQ(c, 123);
CAF_CHECK_NOT_EQUAL(c, "123"s); VARIANT_EQ(d, uint8_t{252});
CAF_CHECK_EQUAL(d, uint8_t{252});
} }
CAF_TEST(n_ary_visit) { CAF_TEST(n_ary_visit) {
...@@ -152,8 +147,7 @@ CAF_TEST(get_if) { ...@@ -152,8 +147,7 @@ CAF_TEST(get_if) {
CAF_CHECK_EQUAL(get_if<int>(&b), nullptr); CAF_CHECK_EQUAL(get_if<int>(&b), nullptr);
CAF_CHECK_NOT_EQUAL(get_if<std::string>(&b), nullptr); CAF_CHECK_NOT_EQUAL(get_if<std::string>(&b), nullptr);
CAF_MESSAGE("test get_if via unit test framework"); CAF_MESSAGE("test get_if via unit test framework");
CAF_CHECK_NOT_EQUAL(b, 42); VARIANT_EQ(b, "foo"s);
CAF_CHECK_EQUAL(b, "foo"s);
} }
CAF_TEST(less_than) { CAF_TEST(less_than) {
......
...@@ -46,7 +46,7 @@ instance::instance(abstract_broker* parent, callee& lstnr) ...@@ -46,7 +46,7 @@ instance::instance(abstract_broker* parent, callee& lstnr)
: tbl_(parent), this_node_(parent->system().node()), callee_(lstnr) { : tbl_(parent), this_node_(parent->system().node()), callee_(lstnr) {
CAF_ASSERT(this_node_ != none); CAF_ASSERT(this_node_ != none);
size_t workers; size_t workers;
if (auto workers_cfg = get_if<size_t>(&config(), "caf.middleman.workers")) if (auto workers_cfg = get_as<size_t>(config(), "caf.middleman.workers"))
workers = *workers_cfg; workers = *workers_cfg;
else else
workers = std::min(3u, std::thread::hardware_concurrency() / 4u) + 1; workers = std::min(3u, std::thread::hardware_concurrency() / 4u) + 1;
...@@ -250,7 +250,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf, ...@@ -250,7 +250,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
auto writer = make_callback([&](binary_serializer& sink) { auto writer = make_callback([&](binary_serializer& sink) {
using string_list = std::vector<std::string>; using string_list = std::vector<std::string>;
string_list app_ids; string_list app_ids;
if (auto ids = get_if<string_list>(&config(), if (auto ids = get_as<string_list>(config(),
"caf.middleman.app-identifiers")) "caf.middleman.app-identifiers"))
app_ids = std::move(*ids); app_ids = std::move(*ids);
else else
...@@ -349,7 +349,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl, ...@@ -349,7 +349,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
} }
// Check the application ID. // Check the application ID.
string_list whitelist; string_list whitelist;
if (auto ls = get_if<string_list>(&config(), if (auto ls = get_as<string_list>(config(),
"caf.middleman.app-identifiers")) "caf.middleman.app-identifiers"))
whitelist = std::move(*ls); whitelist = std::move(*ls);
else else
......
...@@ -130,7 +130,7 @@ public: ...@@ -130,7 +130,7 @@ public:
bool start(const config_value::dictionary& cfg) override { bool start(const config_value::dictionary& cfg) override {
// Read port, address and reuse flag from the config. // Read port, address and reuse flag from the config.
uint16_t port = 0; uint16_t port = 0;
if (auto cfg_port = get_if<uint16_t>(&cfg, "port")) { if (auto cfg_port = get_as<uint16_t>(cfg, "port")) {
port = *cfg_port; port = *cfg_port;
} else { } else {
return false; return false;
......
...@@ -46,3 +46,8 @@ ...@@ -46,3 +46,8 @@
#define REQUIRE_GE(lhs, rhs) CAF_REQUIRE_GREATER_OR_EQUAL(lhs, rhs) #define REQUIRE_GE(lhs, rhs) CAF_REQUIRE_GREATER_OR_EQUAL(lhs, rhs)
#define MESSAGE(what) CAF_MESSAGE(what) #define MESSAGE(what) CAF_MESSAGE(what)
#define BEGIN_FIXTURE_SCOPE(fixture_class) \
CAF_TEST_FIXTURE_SCOPE(CAF_UNIFYN(tests), fixture_class)
#define END_FIXTURE_SCOPE() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -483,6 +483,11 @@ bool check(test* parent, const char* file, size_t line, const char* expr, ...@@ -483,6 +483,11 @@ bool check(test* parent, const char* file, size_t line, const char* expr,
return result; return result;
} }
bool check_un(bool result, const char* file, size_t line, const char* expr);
bool check_bin(bool result, const char* file, size_t line, const char* expr,
const std::string& lhs, const std::string& rhs);
} // namespace detail } // namespace detail
} // namespace caf::test } // namespace caf::test
...@@ -547,13 +552,8 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -547,13 +552,8 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
#define CAF_CHECK(...) \ #define CAF_CHECK(...) \
([](bool expr_result) { \ ([](bool expr_result) { \
auto caf_check_res \ return ::caf::test::detail::check_un(expr_result, __FILE__, __LINE__, \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \ #__VA_ARGS__); \
__FILE__, __LINE__, #__VA_ARGS__, false, \
expr_result); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
return caf_check_res; \
})(static_cast<bool>(__VA_ARGS__)) })(static_cast<bool>(__VA_ARGS__))
#define CAF_CHECK_FUNC(func, x_expr, y_expr) \ #define CAF_CHECK_FUNC(func, x_expr, y_expr) \
...@@ -642,32 +642,81 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -642,32 +642,81 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
// -- CAF_CHECK* predicate family ---------------------------------------------- // -- CAF_CHECK* predicate family ----------------------------------------------
#define CAF_CHECK_EQUAL(x, y) CAF_CHECK_FUNC(::caf::test::equal_to, x, y) #define CAF_CHECK_EQUAL(x_expr, y_expr) \
([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val == y_val, __FILE__, __LINE__, \
#x_expr " == " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_NOT_EQUAL(x, y) \ #define CAF_CHECK_NOT_EQUAL(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::not_equal_to, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val != y_val, __FILE__, __LINE__, \
#x_expr " != " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_LESS(x, y) CAF_CHECK_FUNC(::caf::test::less_than, x, y) #define CAF_CHECK_LESS(x_expr, y_expr) \
([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val < y_val, __FILE__, __LINE__, \
#x_expr " < " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_NOT_LESS(x, y) \ #define CAF_CHECK_NOT_LESS(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::less_than>, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val < y_val), __FILE__, __LINE__, "not " #x_expr " < " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_LESS_OR_EQUAL(x, y) \ #define CAF_CHECK_LESS_OR_EQUAL(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::less_than_or_equal, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val <= y_val, __FILE__, __LINE__, \
#x_expr " <= " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_NOT_LESS_OR_EQUAL(x, y) \ #define CAF_CHECK_NOT_LESS_OR_EQUAL(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::less_than_or_equal>, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val <= y_val), __FILE__, __LINE__, "not " #x_expr " <= " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_GREATER(x, y) CAF_CHECK_FUNC(::caf::test::greater_than, x, y) #define CAF_CHECK_GREATER(x_expr, y_expr) \
([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val > y_val, __FILE__, __LINE__, \
#x_expr " > " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER(x, y) \ #define CAF_CHECK_NOT_GREATER(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::greater_than>, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val > y_val), __FILE__, __LINE__, "not " #x_expr " > " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_GREATER_OR_EQUAL(x, y) \ #define CAF_CHECK_GREATER_OR_EQUAL(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::greater_than_or_equal, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val >= y_val, __FILE__, __LINE__, \
#x_expr " >= " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER_OR_EQUAL(x, y) \ #define CAF_CHECK_NOT_GREATER_OR_EQUAL(x_expr, y_expr) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::greater_than_or_equal>, x, y) ([](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val >= y_val), __FILE__, __LINE__, "not " #x_expr " >= " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
})(x_expr, y_expr)
// -- CAF_CHECK* predicate family ---------------------------------------------- // -- CAF_CHECK* predicate family ----------------------------------------------
......
...@@ -165,6 +165,51 @@ bool check(test* parent, const char* file, size_t line, const char* expr, ...@@ -165,6 +165,51 @@ bool check(test* parent, const char* file, size_t line, const char* expr,
return result; return result;
} }
bool check_un(bool result, const char* file, size_t line, const char* expr) {
string_view rel_up = "../";
while (strncmp(file, rel_up.data(), rel_up.size()) == 0)
file += rel_up.size();
auto parent = engine::current_test();
auto out = logger::instance().massive();
if (result) {
out << term::green << "** " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::reset << "passed" << '\n';
parent->pass();
} else {
out << term::red << "!! " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::red
<< "check failed: " << expr << term::reset << '\n';
parent->fail(false);
}
engine::last_check_file(file);
engine::last_check_line(line);
return result;
}
bool check_bin(bool result, const char* file, size_t line, const char* expr,
const std::string& lhs, const std::string& rhs) {
string_view rel_up = "../";
while (strncmp(file, rel_up.data(), rel_up.size()) == 0)
file += rel_up.size();
auto parent = engine::current_test();
auto out = logger::instance().massive();
if (result) {
out << term::green << "** " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::reset << "passed" << '\n';
parent->pass();
} else {
out << term::red << "!! " << term::blue << file << term::yellow << ":"
<< term::blue << line << fill(line) << term::red
<< "check failed: " << expr << term::reset << '\n'
<< " lhs: " << lhs << '\n'
<< " rhs: " << rhs << '\n';
parent->fail(false);
}
engine::last_check_file(file);
engine::last_check_line(line);
return result;
}
} // namespace detail } // namespace detail
logger::stream::stream(logger& parent, logger::level lvl) logger::stream::stream(logger& parent, logger::level lvl)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment