diff --git a/include/trompeloeil/matcher/range.hpp b/include/trompeloeil/matcher/range.hpp index 64605d0..6777e4a 100644 --- a/include/trompeloeil/matcher/range.hpp +++ b/include/trompeloeil/matcher/range.hpp @@ -21,6 +21,7 @@ #endif #include +#include namespace trompeloeil { @@ -31,6 +32,51 @@ auto make_predicate_matcher(M &m) { return trompeloeil::param_matches(m, std::ref(value)); }); } + +template +struct store_as +{ + using type = T; + template + static type make(C&& c) { return T(std::forward(c));} +}; + +template +struct store_as +{ + using type = mini_span; + static type make(T* p) { return type(p, N);} +}; + +template +using store_as_t = typename store_as::type; + +template +struct value_type {}; + +template +struct value_type +{ + using type = T&; +}; + + +template +struct value_type().begin())>> { + using type = decltype(*std::declval().begin()); +}; + +template +using value_type_t = typename value_type::type; + +template +struct is_range : std::false_type {}; + +template +struct is_range>> : std::true_type {}; + +template +constexpr bool is_range_v = is_range::value; } namespace impl { @@ -78,11 +124,12 @@ struct is_printer template < typename Type = trompeloeil::wildcard, typename ... Es, + typename = typename std::enable_if || ...)>::type, typename R = make_matcher_return< Type, impl::is_checker, impl::is_printer, - Es...> + impl::store_as_t...> > R range_is(Es&& ... es) { @@ -91,6 +138,50 @@ R range_is(Es&& ... es) impl::is_printer{}, std::forward(es)...); } +namespace impl { +struct is_range_checker{ + template + bool operator()(const R& r, const C& cs) const + { + using std::begin; + using std::end; + return std::equal(begin(r), end(r), begin(cs), end(cs), [](const auto& rv, const auto& cv) + { + return trompeloeil::param_matches(cv, std::ref(rv)); + }); + } +}; +struct is_range_printer{ + template + void operator()(std::ostream& os, const C& cs) const + { + os << " range is {"; + const char* sep = " "; + for (auto& c : cs) { + os << std::exchange(sep, ", ") << c; + } + os << " }"; + } +}; +} +template < + typename Type = trompeloeil::wildcard, + typename C, + typename = impl::value_type_t, + typename R = make_matcher_return< + Type, + impl::is_range_checker, + impl::is_range_printer, + impl::store_as_t> +> +R range_is(C&& c) +{ + return trompeloeil::make_matcher( + impl::is_range_checker{}, + impl::is_range_printer{}, + impl::store_as::make(std::forward(c)) + ); +}; namespace impl { struct is_permutation_checker @@ -146,6 +237,7 @@ struct is_permutation_printer template < typename Type = trompeloeil::wildcard, typename ... Es, + typename = typename std::enable_if || ...)>::type, typename R = make_matcher_return< Type, impl::is_permutation_checker, @@ -160,6 +252,68 @@ R range_is_permutation(Es&& ... es) std::forward(es)...); } +namespace impl { +struct is_permutation_range_checker { + template + bool operator()(const R& range, const C& elements) const { + using std::begin; + using std::end; + auto it = begin(range); + const auto e = end(range); + using element_type = decltype(*it); + std::vector> matchers; + for (const auto& element : elements) { + matchers.push_back(impl::make_predicate_matcher(element)); + } + while (it != e) { + auto found = + std::find_if(matchers.begin(), matchers.end(), + [it](const auto &matcher) { return matcher(*it); }); + if (found == matchers.end()) { + break; + } + *found = std::move(matchers.back()); + matchers.pop_back(); + ++it; + } + return it == e && matchers.empty(); + } +}; + +struct is_permutation_range_printer { + template + void operator()(std::ostream& os, const C& c) const + { + os << " range is permutation of {"; + const char* sep = " "; + for (const auto& v : c) + { + os << std::exchange(sep, ", ") << v; + }; + + os << " }"; + } + +}; +} + +template < + typename Type = trompeloeil::wildcard, + typename C, + typename R = make_matcher_return< + Type, + impl::is_permutation_range_checker, + impl::is_permutation_range_printer, + impl::store_as_t> + > +R range_is_permutation(C&& c) +{ + return trompeloeil::make_matcher( + impl::is_permutation_range_checker{}, + impl::is_permutation_range_printer{}, + impl::store_as::make(std::forward(c))); +} + namespace impl { struct has_checker { @@ -212,6 +366,7 @@ struct has_printer template < typename Type = trompeloeil::wildcard, typename ... Es, + typename = typename std::enable_if && ...)>::type, typename R = make_matcher_return< Type, impl::has_checker, @@ -226,6 +381,69 @@ R range_has(Es&& ... es) std::forward(es)...); } +namespace impl { +struct has_range_checker { + template + bool operator()(const R& range, const C& elements) const + { + using std::begin; + using std::end; + auto it = begin(range); + const auto e = end(range); + using element_type = decltype(*it); + std::vector> matchers; + for (auto& element : elements) + { + matchers.push_back(make_predicate_matcher(element)); + } + while (it != e) + { + auto found = std::find_if(matchers.begin(), matchers.end(), + [it](const auto& matcher) { + return matcher(*it); + }); + if (found != matchers.end()) { + *found = std::move(matchers.back()); + matchers.pop_back(); + } + ++it; + } + return matchers.empty(); + } + +}; +struct has_range_printer { + template + void operator()(std::ostream& os, const E& elements) const + { + os << " range has {"; + const char* sep = ""; + for (const auto& v : elements) { + os << std::exchange(sep, ", ") << v; + }; + os << " }"; + } + +}; +} +template < + typename Type = trompeloeil::wildcard, + typename C, + typename = typename std::enable_if>::type, + typename R = make_matcher_return< + Type, + impl::has_range_checker, + impl::has_range_printer, + impl::store_as_t> + > +R range_has(C&& c) +{ + return trompeloeil::make_matcher( + impl::has_range_checker{}, + impl::has_range_printer{}, + impl::store_as::make(std::forward(c))); +} + namespace impl { struct is_all_checker @@ -412,6 +630,7 @@ struct starts_with_printer template < typename Type = trompeloeil::wildcard, typename ... Es, + typename = typename std::enable_if && ...)>::type, typename R = make_matcher_return< Type, impl::starts_with_checker, @@ -425,6 +644,55 @@ R range_starts_with(Es&& ... es) std::forward(es)...); } +namespace impl { +struct starts_with_range_checker{ + template + bool operator()(const R& range, const E& elements) const + { + using std::begin; + using std::end; + auto result = std::mismatch(begin(range), end(range), + begin(elements),end(elements), + [](const auto& t, const auto& c) { + return trompeloeil::param_matches(c, std::ref(t)); + }); + return result.second == end(elements); + } + +}; +struct starts_with_range_printer{ + template + void operator()(std::ostream& os, const E& elements) const + { + os << " range starts with {"; + const char* sep = " "; + for (const auto& v : elements) { + os << std::exchange(sep, ", ") << v; + } + os << " }"; + + } + +}; +} + +template < + typename Type = trompeloeil::wildcard, + typename C, + typename = typename std::enable_if>::type, + typename R = make_matcher_return< + Type, + impl::starts_with_range_checker, + impl::starts_with_range_printer, + impl::store_as_t>> +R range_starts_with(C&& c) +{ + return trompeloeil::make_matcher( + impl::starts_with_range_checker{}, + impl::starts_with_range_printer{}, + impl::store_as::make(std::forward(c))); +} + namespace impl { struct ends_with_checker { @@ -479,6 +747,7 @@ struct ends_with_printer template < typename Type = trompeloeil::wildcard, typename ... Es, + typename = typename std::enable_if || ...)>::type, typename R = make_matcher_return< Type, impl::ends_with_checker, @@ -493,5 +762,63 @@ R range_ends_with(Es&& ... es) std::forward(es)...); } +namespace impl +{ +struct ends_with_range_checker{ + template + bool operator()(const R &range, const E& elements) const + { + using std::begin; + using std::end; + auto it = begin(range); + const auto e = end(range); + const auto num_values = static_cast(elements.size()); + const auto size = std::distance(it, e); + if (size < num_values) + { + return false; + } + std::advance(it, size - num_values); + auto result = std::mismatch(it, e, + begin(elements), end(elements), + [](const auto& v, const auto& c){ + return trompeloeil::param_matches(c, std::ref(v)); + }); + return result.second == elements.end(); + } +}; + +struct ends_with_range_printer{ + template + void operator()(std::ostream &os, const E& elements) const + { + os << " range ends with {"; + const char *sep = " "; + for (const auto& e : elements) { + os << std::exchange(sep, ", ") << e; + }; + os << " }"; + } +}; +} + +template < + typename Type = trompeloeil::wildcard, + typename C, + typename = typename std::enable_if>::type, + typename R = make_matcher_return< + Type, + impl::ends_with_range_checker, + impl::ends_with_range_printer, + impl::store_as_t> + > +R range_ends_with(C&& c) +{ + return trompeloeil::make_matcher( + impl::ends_with_range_checker{}, + impl::ends_with_range_printer{}, + impl::store_as::make(std::forward(c))); +} + } #endif // TROMPELOEIL_RANGE_HPP diff --git a/include/trompeloeil/mock.hpp b/include/trompeloeil/mock.hpp index 9fafbf5..68632bb 100644 --- a/include/trompeloeil/mock.hpp +++ b/include/trompeloeil/mock.hpp @@ -516,6 +516,7 @@ namespace trompeloeil { mini_span(T* address, size_t size) noexcept : begin_(address), end_(address + size) {} T* begin() const noexcept { return begin_; } T* end() const noexcept { return end_; } + size_t size() const { return static_cast(end_ - begin_); } private: T* begin_; T* end_; diff --git a/test/compiling_tests_14.cpp b/test/compiling_tests_14.cpp index 35c23d8..f5ba3f1 100644 --- a/test/compiling_tests_14.cpp +++ b/test/compiling_tests_14.cpp @@ -3578,6 +3578,21 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: elements of a vector can be tested with matcher range_is and a C-array", + "[C++14][matching][matchers][range_is]" +) +{ + { + range_mock m; + int elements[]{1,3,5}; + REQUIRE_CALL(m, vector(trompeloeil::range_is(elements))); + m.vector({1,3,5}); + } + REQUIRE(reports.empty()); +} + TEST_CASE_METHOD( Fixture, "C++14: elements of a vector can be tested with matcher range_is and element matchers", @@ -3595,6 +3610,61 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: a mismatching range for range_is is reported", + "[C++14][matching][matchers][range_is]" +) +{ + try { + range_mock m; + using trompeloeil::range_is; + using trompeloeil::ge; + REQUIRE_CALL(m, vector(range_is(ge(1),3,5))); + m.vector({1,3, 4}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_is\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is \{ >= 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + +TEST_CASE_METHOD( + Fixture, + "C++14: a mismatching range for range_is and a C-array is reported", + "[C++14][matching][matchers][range_is]" +) +{ + try { + range_mock m; + using trompeloeil::range_is; + int expected[] { 1, 3, 5 }; + REQUIRE_CALL(m, vector(range_is(expected))); + m.vector({1,3, 4}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_is\(expected\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + + TEST_CASE_METHOD( Fixture, "C++14: a too short range for range_is is reported", @@ -3622,6 +3692,33 @@ Tried m\.vector\(range_is\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* } } +TEST_CASE_METHOD( + Fixture, + "C++14: a too short range for range_is and a C-array is reported", + "[C++14][matching][matchers][range_is]" +) +{ + try { + range_mock m; + using trompeloeil::range_is; + int expected[] { 1, 3, 5 }; + REQUIRE_CALL(m, vector(range_is(expected))); + m.vector({1,3}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3 \} + +Tried m\.vector\(range_is\(expected\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + TEST_CASE_METHOD( Fixture, "C++14: a too long range for range_is is reported", @@ -3649,6 +3746,33 @@ Tried m\.vector\(range_is\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* } } +TEST_CASE_METHOD( + Fixture, + "C++14: a too long range for range_is and a C-array is reported", + "[C++14][matching][matchers][range_is]" +) +{ + try { + range_mock m; + using trompeloeil::range_is; + int expected[] { 1, 3, 5 }; + REQUIRE_CALL(m, vector(range_is(expected))); + m.vector({1,3,5,6}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 5, 6 \} + +Tried m\.vector\(range_is\(expected\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + TEST_CASE_METHOD( Fixture, "C++14: range_is can be disambiguated with explicit type", @@ -3679,6 +3803,21 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: elements of a vector can be tested with matcher range_is_permutation and a C-array", + "[C++14][matching][matchers][range_is_permutation]" +) +{ + { + range_mock m; + int values[] { 3,5,1 }; + REQUIRE_CALL(m, vector(trompeloeil::range_is_permutation(values))); + m.vector({1,3,5}); + } + REQUIRE(reports.empty()); +} + TEST_CASE_METHOD( Fixture, "C++14: elements of a vector can be tested with matcher range_is_permutation and element matchers", @@ -3696,6 +3835,59 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: a mismatching range for range_is_permutation is reported", + "[C++14][matching][matchers][range_is_permutation]" +) +{ + try { + range_mock m; + using trompeloeil::range_is_permutation; + REQUIRE_CALL(m, vector(range_is_permutation(1, 5, 3))); + m.vector({1, 3, 4}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_is_permutation\(1, 5, 3\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is permutation of \{1, 5, 3 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + +TEST_CASE_METHOD( + Fixture, + "C++14: a mismatcheng range for range_is_permutation and a C-array is reported", + "[C++14][matching][matchers][range_is_permutation]" +) +{ + try { + range_mock m; + using trompeloeil::range_is_permutation; + int values[] { 1, 3, 5}; + REQUIRE_CALL(m, vector(range_is_permutation(values))); + m.vector({1,3,4}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_is_permutation\(values\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is permutation of \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + TEST_CASE_METHOD( Fixture, "C++14: a too short range for range_is_permutation is reported", @@ -3723,6 +3915,33 @@ Tried m\.vector\(range_is_permutation\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0- } } +TEST_CASE_METHOD( + Fixture, + "C++14: a too short range for range_is_permutation and a C-array is reported", + "[C++14][matching][matchers][range_is_permutation]" +) +{ + try { + range_mock m; + using trompeloeil::range_is_permutation; + int values[] { 1, 3, 5}; + REQUIRE_CALL(m, vector(range_is_permutation(values))); + m.vector({1,3}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3 \} + +Tried m\.vector\(range_is_permutation\(values\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is permutation of \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + TEST_CASE_METHOD( Fixture, "C++14: a too long range for range_is_permutation is reported", @@ -3750,6 +3969,33 @@ Tried m\.vector\(range_is_permutation\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0- } } +TEST_CASE_METHOD( + Fixture, + "C++14: a too long range for range_is_permutation and a C-array is reported", + "[C++14][matching][matchers][range_is_permutation]" +) +{ + try { + range_mock m; + using trompeloeil::range_is_permutation; + int expected[]{1,3,5}; + REQUIRE_CALL(m, vector(range_is_permutation(expected))); + m.vector({1,3,5,6}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 5, 6 \} + +Tried m\.vector\(range_is_permutation\(expected\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range is permutation of \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + TEST_CASE_METHOD( Fixture, "C++14: range_is_permutation can be disambiguated with explicit type", @@ -3781,6 +4027,21 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: elements of a vector can be tested with matcher range_starts_with and a C-array", + "[C++14][matching][matchers][range_starts_with]" +) +{ + { + range_mock m; + int first[] { 1,3 }; + REQUIRE_CALL(m, vector(trompeloeil::range_starts_with(first))); + m.vector({1,3,5}); + } + REQUIRE(reports.empty()); +} + TEST_CASE_METHOD( Fixture, "C++14: elements of a vector can be tested with matcher range_starts_with and element matchers", @@ -3824,6 +4085,87 @@ Tried m\.vector\(range_starts_with\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]* } } +TEST_CASE_METHOD( + Fixture, + "C++14: a too short range for range_starts_with and a C-array is reported", + "[C++14][matching][matchers][range_starts_with]" +) +{ + try { + range_mock m; + using trompeloeil::range_starts_with; + int tail[] { 1, 3, 5 }; + REQUIRE_CALL(m, vector(range_starts_with(tail))); + m.vector({1,3}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3 \} + +Tried m\.vector\(range_starts_with\(tail\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range starts with \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + +TEST_CASE_METHOD( + Fixture, + "C++14: a mismatching range for range_starts_with is reported", + "[C++14][matching][matchers][range_starts_with]" +) +{ + try { + range_mock m; + using trompeloeil::range_starts_with; + using trompeloeil::ge; + REQUIRE_CALL(m, vector(range_starts_with(ge(1), 3, 5))); + m.vector({1, 3, 4}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_starts_with\(ge\(1\), 3, 5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range starts with \{ >= 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + +TEST_CASE_METHOD( + Fixture, + "C++14: a mismatching range for range_starts_with and a C-array is reported", + "[C++14][matching][matchers][range_starts_with]" +) +{ + try { + range_mock m; + using trompeloeil::range_starts_with; + int tail[] { 1, 3, 5 }; + REQUIRE_CALL(m, vector(range_starts_with(tail))); + m.vector({1, 3, 4}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_starts_with\(tail\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range starts with \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + TEST_CASE_METHOD( Fixture, @@ -3855,6 +4197,21 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: elements of a vector can be tested with matcher range_ends_with and a C-array", + "[C++14][matching][matchers][range_ends_with]" +) +{ + { + range_mock m; + int tail[]{3,5}; + REQUIRE_CALL(m, vector(trompeloeil::range_ends_with(tail))); + m.vector({1,3,5}); + } + REQUIRE(reports.empty()); +} + TEST_CASE_METHOD( Fixture, "C++14: elements of a vector can be tested with matcher range_ends_with and element matchers", @@ -3898,6 +4255,86 @@ Tried m\.vector\(range_ends_with\(ge\(1\),3,5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* } } +TEST_CASE_METHOD( + Fixture, + "C++14: a too short range for range_ends_with and a C-array is reported", + "[C++14][matching][matchers][range_ends_with]" +) +{ + try { + range_mock m; + using trompeloeil::range_ends_with; + int tail[] { 1,3,5}; + REQUIRE_CALL(m, vector(range_ends_with(tail))); + m.vector({1,3}); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3 \} + +Tried m\.vector\(range_ends_with\(tail\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range ends with \{ 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + +TEST_CASE_METHOD( + Fixture, + "C++14: mismatching range for range_ends_with is reported", + "[C++14][matching][matchers][range_ends_with]" +) +{ + try { + range_mock m; + using trompeloeil::range_ends_with; + using trompeloeil::ge; + REQUIRE_CALL(m, vector(range_ends_with(ge(1), 3, 5))); + m.vector({ 1, 3, 4 }); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 4 \} + +Tried m\.vector\(range_ends_with\(ge\(1\), 3, 5\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range ends with \{ >= 1, 3, 5 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} + +TEST_CASE_METHOD( + Fixture, + "C++14: mismatching range for range_ends_with and a C-array is reported", + "[C++14][matching][matchers][range_ends_with]" +) +{ + try { + range_mock m; + using trompeloeil::range_ends_with; + int tail[] { 1, 3, 4}; + REQUIRE_CALL(m, vector(range_ends_with(tail))); + m.vector({1, 3, 5 }); + FAIL("didn't throw"); + } + catch(reported) { + REQUIRE(reports.size() == 1U); + INFO("report=" << reports.front().msg); + auto re = R":(No match for call of vector with signature void\(const std::vector&\) with\. + param _1 == \{ 1, 3, 5 \} + +Tried m\.vector\(range_ends_with\(tail\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.* + Expected _1 range ends with \{ 1, 3, 4 \}):"; + + REQUIRE(std::regex_search(reports.front().msg, std::regex(re))); + } +} TEST_CASE_METHOD( Fixture, @@ -3930,6 +4367,21 @@ TEST_CASE_METHOD( REQUIRE(reports.empty()); } +TEST_CASE_METHOD( + Fixture, + "C++14: elements of a vector can be tested with matcher range_has and a C-array", + "[C++14][matching][matchers][range_has]" +) +{ + { + range_mock m; + int values[] { 5, 1 }; + REQUIRE_CALL(m, vector(trompeloeil::range_has(values))); + m.vector({1,3,5}); + } + REQUIRE(reports.empty()); +} + TEST_CASE_METHOD( Fixture, "C++14: elements of a vector can be tested with matcher range_has and element matchers",