17constexpr std::string_view get_type_name() {
18 std::string_view name;
19#if defined(__clang__) || defined(__GNUC__)
20 name = __PRETTY_FUNCTION__;
21#elif defined(_MSC_VER)
24 return "unknown_type";
28 size_t start = name.find(
"T = ");
29 if (start != std::string_view::npos) {
33 size_t end = name.find_first_of(
";]", start);
34 if (end != std::string_view::npos) {
35 return name.substr(start, end - start);
40 start = name.find_last_of(
'<');
41 size_t end = name.find_last_of(
">]");
42 if (start != std::string_view::npos && end != std::string_view::npos && end > start) {
43 return name.substr(start + 1, end - start - 1);
50concept Printable =
requires(std::stringstream ss,
const std::remove_cvref_t<T> & value) {
55concept OptionalOrExpected =
requires(
const T & t){
56 {t.has_value()} -> std::convertible_to<bool>;
57 {t.value()} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
61concept AssociativeContainer =
requires(std::remove_cvref_t<T> container,
typename std::remove_cvref_t<T>::key_type key) {
62 {container.find(key)} -> std::same_as<typename std::remove_cvref_t<T>::iterator>;
65static inline void printValue(std::stringstream & ss,
const auto & value) {
66 if constexpr (Printable<std::remove_cvref_t<
decltype(value)>>) {
73static std::string printContainer(std::ranges::input_range
auto && container) {
74 std::pair<char, char> containerBoundary = std::make_pair(
'[',
']');
75 if constexpr (AssociativeContainer<std::remove_cvref_t<
decltype(container)>>) {
76 containerBoundary = std::make_pair(
'{',
'}');
79 ss << containerBoundary.first;
81 for(
const auto & element : container) {
85 printValue(ss, element);
88 ss << containerBoundary.second;
92static auto message(
auto... vals) {
95 ((printValue(ss, vals)), ...);
101 std::stringstream ss;
103 ss <<
"Stack trace:" << std::endl;
104 auto st = std::stacktrace::current();
105 auto it = st.begin();
106 while(it != st.end() && it->source_file().ends_with(
"TestUtil.hpp"))
109 while(not it->source_file().ends_with(
"gtest.cc") && it != st.end()) {
112 ss <<
"\t" << it->source_file() <<
":" << it->source_line();
119static auto messageWithTrace(
auto... vals) {
120 std::stringstream ss;
121 ss << message(vals...) << std::endl;
126template<
typename T,
typename Eq = std::equal_to<>>
127static testing::AssertionResult contains(std::ranges::input_range
auto && collection,
const T & element, Eq
const & eq = Eq{}) {
128 if(std::ranges::end(collection) != std::ranges::find_if(collection, [&element, &eq](
const auto & e) {
return eq(e, element); })){
129 return testing::AssertionSuccess();
131 return testing::AssertionFailure() << message(
"Collection ", printContainer(collection),
" does not contain element: ", element);
135template<
typename T,
typename Eq = std::equal_to<>>
136static testing::AssertionResult notContains(std::ranges::input_range
auto && collection,
const T & element, Eq
const & eq = Eq{}) {
137 if(std::ranges::end(collection) == std::ranges::find_if(collection, [&element, &eq](
const auto & e) {
return eq(e, element); })){
138 return testing::AssertionSuccess();
140 return testing::AssertionFailure() << message(
"Collection ", printContainer(collection),
" contains element: ", element);
144template<
typename Eq = std::equal_to<>>
145static testing::AssertionResult unsortedRangeEqual(std::ranges::forward_range
auto const & actual, std::ranges::forward_range
auto const & expected, Eq
const & eq = Eq{}) {
146 using A = std::ranges::range_value_t<
decltype(actual)>;
147 using E = std::ranges::range_value_t<
decltype(expected)>;
148 static_assert(
fishnet::util::BiFunction<
decltype(eq), A, E,
bool>,
"eq must be a binary function that takes two arguments of type A and E");
152 for(
const auto & expectedElement : expected){
153 auto result = contains(actual, expectedElement, eq);
158 return testing::AssertionSuccess() << message(
"Ranges are equal (ignoring order)");
161template<
typename Eq = std::equal_to<>>
162static testing::AssertionResult sortedRangeEqual(std::ranges::forward_range
auto const & actual, std::ranges::forward_range
auto const & expected, Eq
const & eq = Eq{}) {
163 using A = std::ranges::range_value_t<
decltype(actual)>;
164 using E = std::ranges::range_value_t<
decltype(expected)>;
165 static_assert(std::convertible_to<A,E>,
"Actual and expected collection must have convertible value type");
166 static_assert(
fishnet::util::BiFunction<
decltype(eq), A, E,
bool>,
"eq must be a binary function that takes two arguments of type A and E");
167 auto actualIt = std::ranges::cbegin(actual);
168 auto expectedIt = std::ranges::cbegin(expected);
169 while(actualIt != std::ranges::cend(actual) && expectedIt != std::ranges::cend(expected)){
170 if(!eq(*actualIt,*expectedIt)){
171 if constexpr (Printable<A> && Printable<E>) {
172 return testing::AssertionFailure() << message(
"Ranges differ at index ", std::distance(std::ranges::cbegin(actual), actualIt),
"! Expecting ", *expectedIt,
" but was: ", *actualIt);
174 return testing::AssertionFailure() << message(
"Ranges differ at index ", std::distance(std::ranges::cbegin(actual), actualIt));
180 if(actualIt != std::ranges::cend(actual) or expectedIt != std::ranges::cend(expected)) {
183 return testing::AssertionSuccess() << message(
"Ranges are equal (in order)");
188 FAIL() << __impl::messageWithTrace(
"Test not implemented yet");
193 EXPECT_TRUE(__impl::contains(collection,element)) << __impl::trace();
198 EXPECT_TRUE(__impl::notContains(collection,element)) << __impl::trace();
201static void EXPECT_CONTAINS_ALL(std::ranges::input_range
auto && collection, std::ranges::input_range
auto && expected){
202 for(
const auto & element : expected){
203 EXPECT_TRUE(__impl::contains(collection,element)) << __impl::trace();
207template<
typename... Args>
212static void EXPECT_UNSORTED_RANGE_EQ(std::ranges::forward_range
auto const & actual, std::ranges::forward_range
auto const & expected) {
213 EXPECT_TRUE(__impl::unsortedRangeEqual(actual, expected)) << __impl::trace();
216static void EXPECT_UNSORTED_RANGE_EQ(std::ranges::forward_range
auto const & actual, std::ranges::forward_range
auto const & expected,
auto const & eq) {
217 EXPECT_TRUE(__impl::unsortedRangeEqual(actual, expected, eq)) << __impl::trace();
220static void EXPECT_SORTED_RANGE_EQ(std::ranges::forward_range
auto const & actual, std::ranges::forward_range
auto const & expected) {
221 EXPECT_TRUE(__impl::sortedRangeEqual(actual, expected)) << __impl::trace();
224static void EXPECT_SORTED_RANGE_EQ(std::ranges::forward_range
auto const & actual, std::ranges::forward_range
auto const & expected,
auto const & eq) {
225 EXPECT_TRUE(__impl::sortedRangeEqual(actual, expected, eq)) << __impl::trace();
228static void EXPECT_SIZE(std::ranges::range
auto && range,
size_t expectedSize) {
233 EXPECT_EQ(
fishnet::util::size(range), 0) << __impl::messageWithTrace(
"Expected range to be empty but was: ", __impl::printContainer(range));
236template<
typename ExpectedType>
238 if constexpr (not std::is_same_v<std::remove_cvref_t<
decltype(value)>, ExpectedType>){
239 constexpr std::string_view expectedTypeName = __impl::get_type_name<ExpectedType>();
240 constexpr std::string_view actualTypeName = __impl::get_type_name<std::remove_cvref_t<
decltype(value)>>();
241 FAIL() << __impl::messageWithTrace(
"Expected type: '", expectedTypeName,
"' but was: '", actualTypeName,
"'");
247static void EXPECT_VALUE(
const __impl::OptionalOrExpected
auto & wrapper,
const typename std::remove_cvref_t<
decltype(wrapper)>::value_type & expectedValue){
248 EXPECT_TRUE(wrapper.has_value()) << __impl::messageWithTrace(
"Expected wrapper to have value: '", expectedValue,
"' but was empty");
249 if(wrapper.has_value()){
250 if(wrapper.value() == expectedValue){
253 FAIL() << __impl::messageWithTrace(
"Wrapper has value: '", wrapper.value(),
"' but was expecting: '", expectedValue,
"'");
258static void EXPECT_VALUE(
const __impl::OptionalOrExpected
auto & wrapper){
259 EXPECT_TRUE(wrapper.has_value()) << __impl::messageWithTrace(
"Expected wrapper to have value but was empty");
260 if(wrapper.has_value()){
265static void ASSERT_VALUE(
const __impl::OptionalOrExpected
auto & wrapper) {
266 ASSERT_TRUE(wrapper.has_value()) << __impl::messageWithTrace(
"Expected wrapper to have value but was empty");
269static void EXPECT_EMPTY(
const __impl::OptionalOrExpected
auto & wrapper){
270 if(wrapper.has_value()){
271 FAIL() << __impl::messageWithTrace(
"Expected wrapper to be empty but was: '", wrapper.value(),
"'");
278 EXPECT_TRUE(std::filesystem::exists(path)) << __impl::messageWithTrace(
"Expecting path ", path,
" to exist");
282 EXPECT_FALSE(std::filesystem::exists(path)) << __impl::messageWithTrace(
"Expecting path ", path,
" to not exist");
286 EXPECT_TRUE(value >= lower) << __impl::messageWithTrace(
"Expected value ", value,
" to be greater than or equal to lower bound ", lower);
287 EXPECT_TRUE(value < upper) << __impl::messageWithTrace(
"Expected value ", value,
" to be less than upper bound ", upper);