// // Copyright (c) 2019-2021 Kris Jusiak (kris at jusiak dot net) // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #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) #error "[Boost::ext].UT requires support for decltype"; #elif not defined(__cpp_return_type_deduction) #error "[Boost::ext].UT requires support for return type deduction"; #elif not defined(__cpp_deduction_guides) #error "[Boost::ext].UT requires support for return deduction guides"; #elif not defined(__cpp_generic_lambdas) #error "[Boost::ext].UT requires support for generic lambdas"; #elif not defined(__cpp_constexpr) #error "[Boost::ext].UT requires support for constexpr"; #elif not defined(__cpp_alias_templates) #error "[Boost::ext].UT requires support for alias templates"; #elif not defined(__cpp_variadic_templates) #error "[Boost::ext].UT requires support for variadic templates"; #elif not defined(__cpp_fold_expressions) #error "[Boost::ext].UT requires support for return fold expressions"; #elif not defined(__cpp_static_assert) #error "[Boost::ext].UT requires support for static assert"; #else #define BOOST_UT_VERSION 2'0'1 #if defined(__has_builtin) and defined(__GNUC__) and (__GNUC__ < 10) and \ not defined(__clang__) #undef __has_builtin #endif #if not defined(__has_builtin) #if defined(__GNUC__) and (__GNUC__ >= 9) #define __has___builtin_FILE 1 #define __has___builtin_LINE 1 #endif #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 #endif #if defined(__cpp_exceptions) #include #endif #if __has_include() #include #endif #if __has_include() #include #endif 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; template class function { public: constexpr function() = default; template constexpr /*explicit(false)*/ function(T data) : invoke_{invoke_impl}, destroy_{destroy_impl}, data_{new T{static_cast(data)}} {} constexpr function(function&& other) noexcept : invoke_{static_cast(other.invoke_)}, destroy_{static_cast(other.destroy_)}, data_{static_cast(other.data_)} { other.data_ = {}; } constexpr function(const function&) = delete; ~function() { destroy_(data_); } constexpr function& operator=(const function&) = delete; constexpr function& operator=(function&&) = delete; [[nodiscard]] constexpr auto operator()(TArgs... args) -> R { return invoke_(data_, args...); } [[nodiscard]] constexpr auto operator()(TArgs... args) const -> R { return invoke_(data_, args...); } private: template [[nodiscard]] static auto invoke_impl(void* data, TArgs... args) -> R { return (*static_cast(data))(args...); } template static auto destroy_impl(void* data) -> void { delete static_cast(data); } R (*invoke_)(void*, TArgs...){}; void (*destroy_)(void*){}; void* data_{}; }; [[nodiscard]] inline auto is_match(std::string_view input, std::string_view pattern) -> bool { if (std::empty(pattern)) { return std::empty(input); } if (std::empty(input)) { return pattern[0] == '*' ? is_match(input, pattern.substr(1)) : false; } if (pattern[0] != '?' and pattern[0] != '*' and pattern[0] != input[0]) { return false; } if (pattern[0] == '*') { for (decltype(std::size(input)) i = 0u; i <= std::size(input); ++i) { if (is_match(input.substr(i), pattern.substr(1))) { return true; } } return false; } return is_match(input.substr(1), pattern.substr(1)); } template [[nodiscard]] constexpr auto match(const TPattern& pattern, const TStr& str) -> std::vector { std::vector groups{}; auto pi = 0u; auto si = 0u; const auto matcher = [&](char b, char e, char c = 0) { const auto match = si; while (str[si] and str[si] != b and str[si] != c) { ++si; } groups.emplace_back(str.substr(match, si - match)); while (pattern[pi] and pattern[pi] != e) { ++pi; } pi++; }; while (pi < std::size(pattern) && si < std::size(str)) { if (pattern[pi] == '\'' and str[si] == '\'' and pattern[pi + 1] == '{') { ++si; matcher('\'', '}'); } else if (pattern[pi] == '{') { matcher(' ', '}', ','); } else if (pattern[pi] != str[si]) { return {}; } ++pi; ++si; } if (si < str.size() or pi < std::size(pattern)) { return {}; } return groups; } template [[nodiscard]] inline auto split(T input, TDelim delim) -> std::vector { std::vector output{}; std::size_t first{}; while (first < std::size(input)) { const auto second = input.find_first_of(delim, first); if (first != second) { output.emplace_back(input.substr(first, second - first)); } if (second == T::npos) { break; } first = second + 1; } 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 { #if defined(__cpp_lib_source_location) using source_location = std::source_location; #else class source_location { public: [[nodiscard]] static constexpr auto current( #if (__has_builtin(__builtin_FILE) and __has_builtin(__builtin_LINE)) const char* file = __builtin_FILE(), int line = __builtin_LINE() #else const char* file = "unknown", int line = {} #endif ) noexcept { source_location sl{}; sl.file_ = file; sl.line_ = line; return sl; } [[nodiscard]] constexpr auto file_name() const noexcept { return file_; } [[nodiscard]] constexpr auto line() const noexcept { return line_; } private: const char* file_{"unknown"}; 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 } // 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__) "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 namespace math { template [[nodiscard]] constexpr auto abs(const T t) -> T { return t < T{} ? -t : t; } template [[nodiscard]] constexpr auto abs_diff(const T t, const U u) -> decltype(t < u ? u - t : t - u) { return t < u ? u - t : t - u; } template [[nodiscard]] constexpr auto min_value(const T& lhs, const T& rhs) -> const T& { return (rhs < lhs) ? rhs : lhs; } template [[nodiscard]] constexpr auto pow(const T base, const TExp exp) -> T { return exp ? T(base * pow(base, exp - TExp(1))) : T(1); } template [[nodiscard]] constexpr auto num() -> T { static_assert( ((Cs == '.' or Cs == '\'' or (Cs >= '0' and Cs <= '9')) and ...)); T result{}; for (const char c : std::array{Cs...}) { if (c == '.') { break; } if (c >= '0' and c <= '9') { result = result * T(10) + T(c - '0'); } } return result; } template [[nodiscard]] constexpr auto den() -> T { constexpr const std::array cs{Cs...}; T result{}; auto i = 0u; while (cs[i++] != '.') { } for (auto j = i; j < sizeof...(Cs); ++j) { result += pow(T(10), sizeof...(Cs) - j) * T(cs[j] - '0'); } return result; } template [[nodiscard]] constexpr auto den_size() -> T { constexpr const std::array cs{Cs...}; T i{}; while (cs[i++] != '.') { } return T(sizeof...(Cs)) - i + T(1); } template [[nodiscard]] constexpr auto den_size(TValue value) -> T { constexpr auto precision = TValue(1e-7); T result{}; TValue tmp{}; do { value *= 10; tmp = value - T(value); ++result; } while (tmp > precision); return result; } } // namespace math namespace type_traits { template struct list {}; template struct identity { using type = T; }; template struct function_traits : function_traits {}; template struct function_traits { using result_type = R; using args = list; }; template struct function_traits { using result_type = R; using args = list; }; template struct function_traits { using result_type = R; using args = list; }; template struct function_traits { using result_type = R; using args = list; }; template T&& declval(); template constexpr auto is_valid(TExpr expr) -> decltype(expr(declval()), bool()) { return true; } template constexpr auto is_valid(...) -> bool { return false; } template inline constexpr auto is_range_v = is_valid([](auto t) -> decltype(t.begin(), t.end(), void()) {}); template inline constexpr auto has_user_print = is_valid( [](auto t) -> decltype(void(declval() << t)) {}); template struct has_static_member_object_value : std::false_type {}; template struct has_static_member_object_value().value)>> : std::bool_constant && !std::is_function_v> {}; template inline constexpr bool has_static_member_object_value_v = has_static_member_object_value::value; template struct has_static_member_object_epsilon : std::false_type {}; template struct has_static_member_object_epsilon< T, std::void_t().epsilon)>> : std::bool_constant && !std::is_function_v> {}; template inline constexpr bool has_static_member_object_epsilon_v = has_static_member_object_epsilon::value; template inline constexpr auto is_floating_point_v = false; template <> inline constexpr auto is_floating_point_v = true; template <> inline constexpr auto is_floating_point_v = true; template <> inline constexpr auto is_floating_point_v = true; #if defined(__clang__) or defined(_MSC_VER) template inline constexpr auto is_convertible_v = __is_convertible_to(From, To); #else template constexpr auto is_convertible(int) -> decltype(bool(To(declval()))) { return true; } template constexpr auto is_convertible(...) { return false; } template constexpr auto is_convertible_v = is_convertible(0); #endif template struct requires_ {}; template <> struct requires_ { using type = int; }; 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 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::vector tag{}; reflection::source_location location{}; TArg arg{}; Test run{}; constexpr auto operator()() { run_impl(static_cast(run), arg); } constexpr auto operator()() const { run_impl(static_cast(run), arg); } private: static constexpr auto run_impl(Test test, const none&) { test(); } template static constexpr auto run_impl(T test, const TArg& arg) -> decltype(test(arg), void()) { test(arg); } template static constexpr auto run_impl(T test, const TArg&) -> decltype(test.template operator()(), void()) { test.template operator()(); } }; template test(std::string_view, std::string_view, std::string_view, reflection::source_location, TArg, Test) -> test; template struct suite { TSuite run{}; std::string_view name{}; constexpr auto operator()() { run(); } constexpr auto operator()() const { run(); } }; template suite(TSuite) -> suite; 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{}; std::string_view name{}; TArg arg{}; }; template skip(std::string_view, std::string_view, TArg) -> skip; struct test_skip { std::string_view type{}; std::string_view name{}; }; template struct assertion { TExpr expr{}; reflection::source_location location{}; }; template assertion(TExpr, reflection::source_location) -> assertion; template struct assertion_pass { TExpr expr{}; reflection::source_location location{}; }; template assertion_pass(TExpr) -> assertion_pass; template struct assertion_fail { TExpr expr{}; reflection::source_location location{}; }; template assertion_fail(TExpr) -> assertion_fail; struct test_end { std::string_view type{}; std::string_view name{}; }; template struct log { TMsg msg{}; }; template log(TMsg) -> log; struct fatal_assertion {}; struct exception { const char* msg{}; [[nodiscard]] auto what() const -> const char* { return msg; } }; struct summary {}; } // namespace events namespace detail { struct op {}; template struct fatal_; struct fatal { template [[nodiscard]] inline auto operator()(const T& t) const { return detail::fatal_{t}; } }; 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{}; #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 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