diff --git a/include/boost/ut.hpp b/include/boost/ut.hpp index b4347d8c30..2a13307c4f 100644 --- a/include/boost/ut.hpp +++ b/include/boost/ut.hpp @@ -1,3 +1,4 @@ +// clang-format off // // Copyright (c) 2019-2021 Kris Jusiak (kris at jusiak dot net) // @@ -8,14 +9,34 @@ #if defined(__cpp_modules) && !defined(BOOST_UT_DISABLE_MODULE) export module boost.ut; export import std; +#define BOOST_UT_EXPORT export #else #pragma once +#define BOOST_UT_EXPORT #endif #if __has_include() #include // and, or, not, ... #endif +#include +#if defined(_MSC_VER) + #pragma push_macro("min") + #pragma push_macro("max") + #undef min + #undef max +#endif +// Before libc++ 17 had experimental support for format and it required a +// special build flag. Currently libc++ has not implemented all C++20 chrono +// improvements. Therefore doesn't define __cpp_lib_format, instead query the +// library version to detect the support status. +// +// MSVC STL and libstdc++ provide __cpp_lib_format. +#if defined(__cpp_lib_format) or \ + (defined(_LIBCPP_VERSION) and _LIBCPP_VERSION >= 170000) +#define BOOST_UT_HAS_FORMAT +#endif + #if not defined(__cpp_rvalue_references) #error "[Boost::ext].UT requires support for rvalue references"; #elif not defined(__cpp_decltype) @@ -37,7 +58,7 @@ export import std; #elif not defined(__cpp_static_assert) #error "[Boost::ext].UT requires support for static assert"; #else -#define BOOST_UT_VERSION 1'1'9 +#define BOOST_UT_VERSION 2'0'1 #if defined(__has_builtin) and defined(__GNUC__) and (__GNUC__ < 10) and \ not defined(__clang__) @@ -52,13 +73,24 @@ export import std; #define __has_builtin(...) __has_##__VA_ARGS__ #endif +#include #include +#include +#include #include +#include #include +#include +#include #include +#include #include +#include +#include #include +#include #include +#include #if __has_include() and __has_include() #include #include @@ -67,14 +99,18 @@ export import std; #include #endif +#if __has_include() +#include +#endif #if __has_include() #include #endif -#if defined(__cpp_modules) && !defined(BOOST_UT_DISABLE_MODULE) -export -#endif - namespace boost::inline ext::ut::inline v1_1_9 { +struct unique_name_for_auto_detect_prefix_and_suffix_lenght_0123456789_struct_ { +}; + +BOOST_UT_EXPORT +namespace boost::inline ext::ut::inline v2_0_1 { namespace utility { template class function; @@ -202,6 +238,18 @@ template } return output; } +constexpr auto regex_match(const char *str, const char *pattern) -> bool { + if (*pattern == '\0' && *str == '\0') return true; + if (*pattern == '\0' && *str != '\0') return false; + if (*str == '\0' && *pattern != '\0') return false; + if (*pattern == '.') { + return regex_match(str+1, pattern+1); + } + if (*pattern == *str) { + return regex_match(str+1, pattern+1); + } + return false; +} } // namespace utility namespace reflection { @@ -230,22 +278,72 @@ class source_location { int line_{}; }; #endif +namespace detail { +template +[[nodiscard]] constexpr auto get_template_function_name_use_type() + -> const std::string_view { +// for over compiler need over macros +#if defined(_MSC_VER) && !defined(__clang__) + return {&__FUNCSIG__[0], sizeof(__FUNCSIG__)}; +#else + return {&__PRETTY_FUNCTION__[0], sizeof(__PRETTY_FUNCTION__)}; +#endif +} -template -[[nodiscard]] constexpr auto type_name() -> std::string_view { +// decay allows you to highlight a cleaner name +template +[[nodiscard]] constexpr auto get_template_function_name_use_decay_type() + -> const std::string_view { + return get_template_function_name_use_type>(); +} + +inline constexpr const std::string_view raw_type_name = + get_template_function_name_use_decay_type< + unique_name_for_auto_detect_prefix_and_suffix_lenght_0123456789_struct_>(); + +inline constexpr const std::size_t raw_length = raw_type_name.length(); +inline constexpr const std::string_view need_name = #if defined(_MSC_VER) and not defined(__clang__) - return {&__FUNCSIG__[120], sizeof(__FUNCSIG__) - 128}; -#elif defined(__clang_analyzer__) - return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59}; -#elif defined(__clang__) and (__clang_major__ >= 13) and defined(__APPLE__) - return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59}; -#elif defined(__clang__) and (__clang_major__ >= 12) and not defined(__APPLE__) - return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59}; -#elif defined(__clang__) - return {&__PRETTY_FUNCTION__[70], sizeof(__PRETTY_FUNCTION__) - 72}; -#elif defined(__GNUC__) - return {&__PRETTY_FUNCTION__[85], sizeof(__PRETTY_FUNCTION__) - 136}; + "struct " + "unique_name_for_auto_detect_prefix_and_suffix_lenght_0123456789_struct_"; +#else + "unique_name_for_auto_detect_prefix_and_suffix_lenght_0123456789_struct_"; #endif +inline constexpr const std::size_t need_length = need_name.length(); +static_assert(need_length <= raw_length, + "Auto find prefix and suffix lenght broken error 1"); +inline constexpr const std::size_t prefix_length = + raw_type_name.find(need_name); +static_assert(prefix_length != std::string_view::npos, + "Auto find prefix and suffix lenght broken error 2"); +static_assert(prefix_length <= raw_length, + "Auto find prefix and suffix lenght broken error 3"); +inline constexpr const std::size_t tail_lenght = raw_length - prefix_length; +static_assert(need_length <= tail_lenght, + "Auto find prefix and suffix lenght broken error 4"); +inline constexpr const std::size_t suffix_length = tail_lenght - need_length; + +} // namespace detail + +template +[[nodiscard]] constexpr auto type_name() -> const std::string_view { + const std::string_view raw_type_name = + detail::get_template_function_name_use_type(); + const std::size_t end = raw_type_name.length() - detail::suffix_length; + const std::size_t len = end - detail::prefix_length; + std::string_view result = raw_type_name.substr(detail::prefix_length, len); + return result; +} + +// decay allows you to highlight a cleaner name +template +[[nodiscard]] constexpr auto decay_type_name() -> const std::string_view { + const std::string_view raw_type_name = + detail::get_template_function_name_use_decay_type(); + const std::size_t end = raw_type_name.length() - detail::suffix_length; + const std::size_t len = end - detail::prefix_length; + std::string_view result = raw_type_name.substr(detail::prefix_length, len); + return result; } } // namespace reflection @@ -376,11 +474,11 @@ constexpr auto is_valid(...) -> bool { } template -static constexpr auto is_container_v = +inline constexpr auto is_range_v = is_valid([](auto t) -> decltype(t.begin(), t.end(), void()) {}); template -static constexpr auto has_user_print = is_valid( +inline constexpr auto has_user_print = is_valid( [](auto t) -> decltype(void(declval() << t)) {}); template @@ -420,7 +518,7 @@ inline constexpr auto is_floating_point_v = true; #if defined(__clang__) or defined(_MSC_VER) template -static constexpr auto is_convertible_v = __is_convertible_to(From, To); +inline constexpr auto is_convertible_v = __is_convertible_to(From, To); #else template constexpr auto is_convertible(int) -> decltype(bool(To(declval()))) { @@ -445,18 +543,66 @@ template using requires_t = typename requires_::type; } // namespace type_traits +template +struct fixed_string { + constexpr static std::size_t N = SIZE; + CharT _data[N + 1] = {}; + + constexpr explicit(false) fixed_string(const CharT (&str)[N + 1]) noexcept { + if constexpr (N != 0) + for (std::size_t i = 0; i < N; ++i) _data[i] = str[i]; + } + + [[nodiscard]] constexpr std::size_t size() const noexcept { return N; } + [[nodiscard]] constexpr bool empty() const noexcept { return N == 0; } + [[nodiscard]] constexpr explicit operator std::string_view() const noexcept { + return {_data, N}; + } + [[nodiscard]] explicit operator std::string() const noexcept { + return {_data, N}; + } + [[nodiscard]] operator const char*() const noexcept { return _data; } + [[nodiscard]] constexpr bool operator==( + const fixed_string& other) const noexcept { + return std::string_view{_data, N} == std::string_view(other); + } + + template + [[nodiscard]] friend constexpr bool operator==( + const fixed_string&, const fixed_string&) { + return false; + } +}; + +template +fixed_string(const CharT (&str)[N]) -> fixed_string; + struct none {}; namespace events { +struct run_begin { + int argc{}; + const char** argv{}; +}; struct test_begin { std::string_view type{}; std::string_view name{}; reflection::source_location location{}; }; +struct suite_begin { + std::string_view type{}; + std::string_view name{}; + reflection::source_location location{}; +}; +struct suite_end { + std::string_view type{}; + std::string_view name{}; + reflection::source_location location{}; +}; template struct test { std::string_view type{}; - std::string_view name{}; + std::string name{}; /// might be dynamic std::vector tag{}; reflection::source_location location{}; TArg arg{}; @@ -486,6 +632,7 @@ test(std::string_view, std::string_view, std::string_view, template struct suite { TSuite run{}; + std::string_view name{}; constexpr auto operator()() { run(); } constexpr auto operator()() const { run(); } }; @@ -495,6 +642,10 @@ struct test_run { std::string_view type{}; std::string_view name{}; }; +struct test_finish { + std::string_view type{}; + std::string_view name{}; +}; template struct skip { std::string_view type{}; @@ -548,1918 +699,2670 @@ struct summary {}; namespace detail { struct op {}; -struct fatal {}; -struct cfg { - static inline reflection::source_location location{}; - static inline bool wip{}; -}; - -template -[[nodiscard]] constexpr auto get_impl(const T& t, int) -> decltype(t.get()) { - return t.get(); -} -template -[[nodiscard]] constexpr auto get_impl(const T& t, ...) -> decltype(auto) { - return t; -} -template -[[nodiscard]] constexpr auto get(const T& t) { - return get_impl(t, 0); -} - -template -struct type_ : op { - template - [[nodiscard]] constexpr auto operator()(const TOther&) const - -> const type_ { - return {}; - } - [[nodiscard]] constexpr auto operator==(type_) -> bool { return true; } - template - [[nodiscard]] constexpr auto operator==(type_) -> bool { - return false; - } - template - [[nodiscard]] constexpr auto operator==(const TOther&) -> bool { - return std::is_same_v; - } - [[nodiscard]] constexpr auto operator!=(type_) -> bool { return false; } - template - [[nodiscard]] constexpr auto operator!=(type_) -> bool { - return true; - } - template - [[nodiscard]] constexpr auto operator!=(const TOther&) -> bool { - return not std::is_same_v; - } -}; - -template -struct value : op { - using value_type = T; - - constexpr /*explicit(false)*/ value(const T& _value) : value_{_value} {} - [[nodiscard]] constexpr explicit operator T() const { return value_; } - [[nodiscard]] constexpr decltype(auto) get() const { return value_; } - - T value_{}; -}; - -template -struct value>> - : op { - using value_type = T; - static inline auto epsilon = T{}; - - constexpr value(const T& _value, const T precision) : value_{_value} { - epsilon = precision; - } - - constexpr /*explicit(false)*/ value(const T& val) - : value{val, T(1) / math::pow(T(10), - math::den_size(val))} {} - [[nodiscard]] constexpr explicit operator T() const { return value_; } - [[nodiscard]] constexpr decltype(auto) get() const { return value_; } - - T value_{}; -}; - -template -class value_location : public detail::value { - public: - constexpr /*explicit(false)*/ value_location( - const T& t, const reflection::source_location& sl = - reflection::source_location::current()) - : detail::value{t} { - cfg::location = sl; - } - - constexpr value_location(const T& t, const T precision, - const reflection::source_location& sl = - reflection::source_location::current()) - : detail::value{t, precision} { - cfg::location = sl; - } -}; - -template -struct integral_constant : op { - using value_type = decltype(N); - static constexpr auto value = N; - - [[nodiscard]] constexpr auto operator-() const { - return integral_constant<-N>{}; - } - [[nodiscard]] constexpr explicit operator value_type() const { return N; } - [[nodiscard]] constexpr auto get() const { return N; } -}; -template -struct floating_point_constant : op { - using value_type = T; - - static constexpr auto epsilon = T(1) / math::pow(T(10), Size - 1); - static constexpr auto value = T(P) * (T(N) + (T(D) / math::pow(T(10), Size))); +template +struct fatal_; - [[nodiscard]] constexpr auto operator-() const { - return floating_point_constant{}; +struct fatal { + template + [[nodiscard]] inline auto operator()(const T& t) const { + return detail::fatal_{t}; } - [[nodiscard]] constexpr explicit operator value_type() const { return value; } - [[nodiscard]] constexpr auto get() const { return value; } }; +struct cfg { + using value_ref = std::variant, + std::reference_wrapper, + std::reference_wrapper>; + using option = std::tuple; + static inline reflection::source_location location{}; + static inline bool wip{}; -template -struct eq_ : op { - constexpr eq_(const TLhs& lhs = {}, const TRhs& rhs = {}) - : lhs_{lhs}, rhs_{rhs}, value_{[&] { - using std::operator==; - using std::operator<; - - if constexpr (type_traits::has_static_member_object_value_v and - type_traits::has_static_member_object_value_v) { - return TLhs::value == TRhs::value; - } else if constexpr (type_traits::has_static_member_object_epsilon_v< - TLhs> and - type_traits::has_static_member_object_epsilon_v< - TRhs>) { - return math::abs(get(lhs) - get(rhs)) < - math::min_value(TLhs::epsilon, TRhs::epsilon); - } else if constexpr (type_traits::has_static_member_object_epsilon_v< - TLhs>) { - return math::abs(get(lhs) - get(rhs)) < TLhs::epsilon; - } else if constexpr (type_traits::has_static_member_object_epsilon_v< - TRhs>) { - return math::abs(get(lhs) - get(rhs)) < TRhs::epsilon; - } else { - return get(lhs) == get(rhs); - } - }()} {} - - [[nodiscard]] constexpr operator bool() const { return value_; } - [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } - [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } - - const TLhs lhs_{}; - const TRhs rhs_{}; - const bool value_{}; -}; +#if defined(_MSC_VER) + static inline int largc = __argc; + static inline const char** largv = const_cast(__argv); +#else + static inline int largc = 0; + static inline const char** largv = nullptr; +#endif -template -struct approx_ : op { - constexpr approx_(const TLhs& lhs = {}, const TRhs& rhs = {}, - const TEpsilon& epsilon = {}) - : lhs_{lhs}, rhs_{rhs}, epsilon_{epsilon}, value_{[&] { - using std::operator<; - - if constexpr (type_traits::has_static_member_object_value_v and - type_traits::has_static_member_object_value_v and - type_traits::has_static_member_object_value_v< - TEpsilon>) { - return math::abs_diff(TLhs::value, TRhs::value) < TEpsilon::value; - } else { - return math::abs_diff(get(lhs), get(rhs)) < get(epsilon); - } - }()} {} - - [[nodiscard]] constexpr operator bool() const { return value_; } - [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } - [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } - [[nodiscard]] constexpr auto epsilon() const { return get(epsilon_); } - - const TLhs lhs_{}; - const TRhs rhs_{}; - const TEpsilon epsilon_{}; - const bool value_{}; -}; + static inline std::string executable_name = "unknown executable"; + static inline std::string query_pattern = ""; // <- done + static inline bool invert_query_pattern = false; // <- done + static inline std::string query_regex_pattern = ""; // <- done + static inline bool show_help = false; // <- done + static inline bool show_tests = false; // <- done + static inline bool list_tags = false; // <- done + static inline bool show_successful_tests = false; // <- done + static inline std::string output_filename = ""; + static inline std::string use_reporter = "console"; // <- done + static inline std::string suite_name = ""; + static inline bool abort_early = false; // <- done + static inline std::size_t abort_after_n_failures = + std::numeric_limits::max(); // <- done + static inline bool show_duration = false; // <- done + static inline std::size_t show_min_duration = 0; + static inline std::string input_filename = ""; + static inline bool show_test_names = false; // <- done + static inline bool show_reporters = false; // <- done + static inline std::string sort_order = "decl"; + static inline std::size_t rnd_seed = 0; // 0: use time + static inline std::string use_colour = "yes"; // <- done + static inline bool show_lib_identity = false; // <- done + static inline std::string wait_for_keypress = "never"; + + static inline const std::vector