// // Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com) // // 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) // // Official repository: https://github.com/boostorg/url // // Test that header file is self-contained. #include #include #include #include #include #include #include "test_rule.hpp" #include #include #include namespace boost { namespace urls { namespace grammar { struct stateless_range_rule { using value_type = core::string_view; system::result first( char const*& it, char const* end) const noexcept { if(it == end) return error::mismatch; return core::string_view(it, 0); } system::result next( char const*&, char const*) const noexcept { return error::end_of_range; } }; struct bad_range_rule { using value_type = core::string_view; }; struct missing_value_type_rule { system::result first( char const*&, char const*) const noexcept { return error::end_of_range; } system::result next( char const*&, char const*) const noexcept { return error::end_of_range; } }; static_assert( is_range_rule>::value, "any_rule must satisfy is_range_rule"); static_assert( is_range_rule::value, "custom range rule must satisfy is_range_rule"); static_assert( ! is_range_rule::value, "trait must reject incomplete implementations"); static_assert( ! is_range_rule::value, "trait must reject types without value_type"); #ifdef BOOST_URL_HAS_CONCEPTS static_assert( RangeRule>, "any_rule must satisfy RangeRule concept"); static_assert( RangeRule, "custom range rule must satisfy RangeRule concept"); static_assert( ! RangeRule, "concept must reject incomplete implementations"); #endif struct range_rule_test { struct big_rule { char unused[4096]{}; using value_type = core::string_view; system::result parse( char const*& it, char const* end) const noexcept { if(it == end) return error::mismatch; if(*it != ';') return error::mismatch; ++it; if(it == end) return error::mismatch; if(*it == ';') return error::mismatch; return core::string_view(it++, 1); } }; struct big_first_rule { char unused[4096]{}; using value_type = core::string_view; system::result parse( char const*& it, char const* end) const noexcept { if(it == end) return error::mismatch; if(*it == ';') return error::mismatch; auto const start = it; while(it != end && *it != ';') ++it; return core::string_view(start, it - start); } }; template static void check( R const& r, core::string_view s, std::initializer_list< core::string_view> init) { auto rv = parse(s, r); if(! BOOST_TEST(rv.has_value())) return; if(! BOOST_TEST_EQ( rv->size(), init.size())) return; BOOST_TEST( std::equal( rv->begin(), rv->end(), init.begin())); } void testRange() { constexpr auto r0 = range_rule( tuple_rule( squelch( delim_rule(';')), token_rule(alpha_chars))); // range() { range v; BOOST_TEST(v.empty()); BOOST_TEST_EQ(v.size(), 0); // move range v2(std::move(v)); BOOST_TEST(v2.empty()); BOOST_TEST_EQ(v2.size(), 0); // copy range v3(v); BOOST_TEST(v3.empty()); BOOST_TEST_EQ(v3.size(), 0); } // range(range&&) { auto v0 = parse(";a;b;c", r0).value(); range v(std::move(v0)); BOOST_TEST(v0.empty()); BOOST_TEST_EQ(v0.size(), 0); BOOST_TEST_EQ(v0.begin(), v0.end()); BOOST_TEST(! v.empty()); BOOST_TEST_EQ(v.size(), 3); BOOST_TEST_EQ(v.string(), ";a;b;c"); } // range(range const&) { auto v0 = parse(";a;b;c", r0).value(); range v(v0); BOOST_TEST(! v0.empty()); BOOST_TEST_EQ(v0.size(), 3); BOOST_TEST_EQ(v0.string(), ";a;b;c"); BOOST_TEST(! v.empty()); BOOST_TEST_EQ(v.size(), 3); BOOST_TEST_EQ(v.string(), ";a;b;c"); } // operator=(range&&) { auto v0 = parse(";a;b;c", r0).value(); auto v1 = parse(";x;y", r0).value(); v1 = std::move(v0); BOOST_TEST(v0.empty()); BOOST_TEST_EQ(v0.size(), 0); BOOST_TEST_EQ(v0.begin(), v0.end()); BOOST_TEST(! v1.empty()); BOOST_TEST_EQ(v1.size(), 3); BOOST_TEST_EQ(v1.string(), ";a;b;c"); } // operator=(range const&) { auto v0 = parse(";a;b;c", r0).value(); auto v1 = parse(";x;y", r0).value(); v1 = v0; BOOST_TEST(! v0.empty()); BOOST_TEST_EQ(v0.size(), 3); BOOST_TEST_EQ(v0.string(), ";a;b;c"); BOOST_TEST(! v1.empty()); BOOST_TEST_EQ(v1.size(), 3); BOOST_TEST_EQ(v1.string(), ";a;b;c"); } // lower limit // upper limit { { constexpr auto r = range_rule( tuple_rule( squelch( delim_rule(';')), token_rule(alpha_chars)), 2, 3); bad(r, "", error::mismatch); bad(r, ";x", error::mismatch); check(r, ";x;y", {"x","y"}); check(r, ";x;y;z", {"x","y","z"}); bad(r, ";a;b;c;d", error::mismatch); bad(r, ";a;b;c;d;e", error::mismatch); } { constexpr auto r = range_rule( token_rule(alpha_chars), tuple_rule( squelch( delim_rule('+')), token_rule(alpha_chars)), 2, 3); bad(r, "", error::mismatch); bad(r, "x", error::mismatch); check(r, "x+y", {"x","y"}); check(r, "x+y+z", {"x","y","z"}); bad(r, "a+b+c+d", error::mismatch); bad(r, "a+b+c+d+e", error::mismatch); } } // big rules { { constexpr auto r = range_rule( big_rule{}, 2, 3); bad(r, "", error::mismatch); bad(r, ";x", error::mismatch); check(r, ";x;y", {"x","y"}); check(r, ";x;y;z", {"x","y","z"}); bad(r, ";a;b;c;d", error::mismatch); bad(r, ";a;b;c;d;e", error::mismatch); } { constexpr auto r = range_rule( big_rule{}, big_rule{}, 2, 3); bad(r, "", error::mismatch); bad(r, "x", error::mismatch); check(r, ";x;y", {"x","y"}); check(r, ";x;y;z", {"x","y","z"}); bad(r, ";a;b;c;d", error::mismatch); bad(r, ";a;b;c;d;e", error::mismatch); } } // big any_rule copies (single rule) { constexpr auto big = range_rule( big_rule{}, 1, 4); auto v = parse(";a;b", big).value(); range copy(v); BOOST_TEST_EQ(copy.size(), v.size()); auto other = parse(";x", big).value(); other = v; BOOST_TEST_EQ(other.size(), v.size()); } // big any_rule copies (first/next pair) { const auto big_pair = range_rule( big_first_rule{}, big_rule{}, 1, 4); auto v = parse("a;b;c", big_pair).value(); range copy(v); BOOST_TEST_EQ(copy.size(), v.size()); auto other = parse("x;y", big_pair).value(); other = v; BOOST_TEST_EQ(other.size(), v.size()); } #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wself-assign-overloaded") || __has_warning("-Wself-move") # pragma clang diagnostic push # if __has_warning("-Wself-assign-overloaded") # pragma clang diagnostic ignored "-Wself-assign-overloaded" # endif # if __has_warning("-Wself-move") # pragma clang diagnostic ignored "-Wself-move" # endif # endif #elif defined(__GNUC__) && !defined(__clang__) # if __GNUC__ >= 13 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wself-move" # endif #endif // any_rule self-assignment (copy) { any_rule ar(big_rule{}); auto& copy_ref = (ar = ar); BOOST_TEST(©_ref == &ar); } // any_rule self-assignment (move) { any_rule ar(big_rule{}); auto& move_ref = (ar = std::move(ar)); BOOST_TEST(&move_ref == &ar); } // range self-assignment (copy) { auto v = parse(";a;b", r0).value(); auto& copy_ref = (v = v); BOOST_TEST(©_ref == &v); } // range self-assignment (move) { auto v = parse(";a;b", r0).value(); auto& move_ref = (v = std::move(v)); BOOST_TEST(&move_ref == &v); } #if defined(__clang__) && defined(__has_warning) # if __has_warning("-Wself-assign-overloaded") || __has_warning("-Wself-move") # pragma clang diagnostic pop # endif #elif defined(__GNUC__) && !defined(__clang__) # if __GNUC__ >= 13 # pragma GCC diagnostic pop # endif #endif // zero-match success path (default N == 0) { constexpr auto r = range_rule( token_rule(alpha_chars)); auto rv = parse("", r); BOOST_TEST(rv.has_value()); BOOST_TEST(rv->empty()); } } void run() { // constexpr { constexpr auto r = range_rule( token_rule(alpha_chars), tuple_rule( squelch( delim_rule('+')), token_rule(alpha_chars))); check(r, "", {}); check(r, "x", {"x"}); } // javadoc { system::result< range > rv = parse( ";alpha;xray;charlie", range_rule( tuple_rule( squelch( delim_rule( ';' ) ), token_rule( alpha_chars ) ), 1 ) ); (void)rv; } // javadoc { system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot", range_rule( token_rule( alpha_chars ), // first tuple_rule( // next squelch( delim_rule(',') ), token_rule( alpha_chars ) ) ) ); (void)rv; } testRange(); } }; TEST_SUITE( range_rule_test, "boost.url.grammar.range_rule"); } // grammar } // urls } // boost