2
0
mirror of https://github.com/boostorg/url.git synced 2026-01-19 16:52:14 +00:00
Files
url/test/unit/params_encoded_ref.cpp
2025-10-23 21:53:22 -07:00

789 lines
24 KiB
C++

//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2022 Alan de Freitas (alandefreitas@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 <boost/url/params_encoded_ref.hpp>
#include <boost/url/parse.hpp>
#include <boost/url/parse_query.hpp>
#include <boost/url/url.hpp>
#include <boost/core/detail/static_assert.hpp>
#include <boost/core/ignore_unused.hpp>
#include "test_suite.hpp"
#include <iterator>
#ifdef assert
#undef assert
#endif
#define assert BOOST_TEST
namespace boost {
namespace urls {
BOOST_CORE_STATIC_ASSERT(
! std::is_default_constructible<
params_encoded_ref>::value);
BOOST_CORE_STATIC_ASSERT(
std::is_copy_constructible<
params_encoded_ref>::value);
BOOST_CORE_STATIC_ASSERT(
std::is_copy_assignable<
params_encoded_ref>::value);
/* Legend
'#' 0x23
'%' 0x25
'&' 0x26
'=' 0x3D
*/
struct params_encoded_ref_test
{
static
bool
is_equal(
param_pct_view const& p0,
param_pct_view const& p1)
{
return
p0.key == p1.key &&
p0.has_value == p1.has_value &&
(! p0.has_value ||
p0.value == p1.value);
}
// check the sequence
static
void
check(
params_encoded_view const& p,
std::initializer_list<
param_pct_view> init)
{
if(! BOOST_TEST_EQ(
p.size(), init.size()))
return;
auto it0 = p.begin();
auto it1 = init.begin();
auto const end = init.end();
while(it1 != end)
{
BOOST_TEST(is_equal(*it0, *it1));
auto tmp = it0++;
BOOST_TEST_EQ(++tmp, it0);
++it1;
}
// reverse
if(init.size() > 0)
{
it0 = p.end();
it1 = init.end();
do
{
auto tmp = it0--;
BOOST_TEST_EQ(--tmp, it0);
--it1;
BOOST_TEST(is_equal(*it0, *it1));
}
while(it1 != init.begin());
}
}
// check that modification produces
// the string and correct sequence
static
void
check(
void(*f)(params_encoded_ref),
core::string_view s0,
core::string_view s1,
std::initializer_list<
param_pct_view> init)
{
url u;
{
auto rv = parse_uri_reference(s0);
if(! BOOST_TEST(rv.has_value()))
return;
u = *rv;
}
params_encoded_ref ps(u.encoded_params());
f(ps);
BOOST_TEST_EQ(u.encoded_query(), s1);
if(! BOOST_TEST_EQ(
ps.size(), init.size()))
return;
check(ps, init);
{
auto rv = parse_query(s1);
if(! BOOST_TEST(rv.has_value()))
return;
check(*rv, init);
}
}
static
void
check(
void(*f1)(params_encoded_ref),
void(*f2)(params_encoded_ref),
core::string_view s0,
core::string_view s1,
std::initializer_list<
param_pct_view> init)
{
check(f1, s0, s1, init);
check(f2, s0, s1, init);
}
//--------------------------------------------
static
void
assign(
params_encoded_ref& qp,
std::initializer_list<
param_view> init)
{
qp.assign(
init.begin(),
init.end());
}
static
auto
append(
params_encoded_ref& qp,
std::initializer_list<
param_view> init) ->
params_encoded_ref::iterator
{
return qp.append(
init.begin(),
init.end());
}
static
auto
insert(
params_encoded_ref& qp,
params_encoded_ref::iterator before,
std::initializer_list<
param_view> init) ->
params_encoded_ref::iterator
{
return qp.insert(
before,
init.begin(),
init.end());
}
static
auto
replace(
params_encoded_ref& qp,
params_encoded_ref::iterator from,
params_encoded_ref::iterator to,
std::initializer_list<
param_view> init) ->
params_encoded_ref::iterator
{
return qp.replace(
from,
to,
init.begin(),
init.end());
}
//--------------------------------------------
void
testSpecial()
{
// params_encoded_ref(params_encoded_ref)
{
url u("?key=value");
params_encoded_ref p0 = u.encoded_params();
BOOST_TEST_EQ(&p0.url(), &u);
params_encoded_ref p1(p0);
BOOST_TEST_EQ(&p0.url(), &p1.url());
check(p0, { {"key","value"} });
check(p1, { {"key","value"} });
}
// operator=(params_encoded_ref)
{
url u0( "?key=value" );
url u1;
params_encoded_ref p0 = u0.encoded_params();
params_encoded_ref p1 = u1.encoded_params();
p1 = p0;
BOOST_TEST_NE(&p0.url(), &p1.url());
check(p0, { {"key","value"} });
check(p1, { {"key","value"} });
}
// operator=(initializer_list)
{
url u;
u.encoded_params() = {
{"first", "John"},
{"last", "Doe"}};
check(u.encoded_params(),{
{"first", "John"},
{"last", "Doe"}});
// reserved character
BOOST_TEST_NO_THROW((u.encoded_params() =
{ {"#", no_value} }));
// invalid pecent-escape
BOOST_TEST_THROWS((u.encoded_params() =
{ {"%", no_value} }), system::system_error);
}
// operator params_encoded_view
{
url u;
params_encoded_view qp = u.encoded_params();
BOOST_TEST_EQ(
qp.buffer().data(), u.buffer().data());
}
}
void
testObservers()
{
// url()
{
url u;
params_encoded_ref qp = u.encoded_params();
BOOST_TEST_EQ(&qp.url(), &u);
}
}
void
testModifiers()
{
// clear()
{
auto const f = [](params_encoded_ref qp)
{
qp.clear();
};
check(f, "", "", {});
check(f, "?", "", {});
check(f, "?first=John&last=Doe", "", {});
}
// assign(initializer_list)
// assign(FwdIt first, FwdIt)
{
auto const f = [](params_encoded_ref qp)
{
qp.assign({ {"first", nullptr}, {"last",""}, {"full", "John Doe"} });
// invalid pecent-escape
BOOST_TEST_THROWS(qp.assign(
{ {"%%", no_value} }), system::system_error);
};
auto const g = [](params_encoded_ref qp)
{
assign(qp, { {"first",nullptr}, {"last",""}, {"full", "John Doe"} });
// invalid pecent-escape
BOOST_TEST_THROWS(qp.assign(
{ {"%%", no_value} }), system::system_error);
};
check(f, g, "", "first&last=&full=John%20Doe",
{ {"first",no_value}, {"last",""}, {"full","John%20Doe"} });
}
// append(param_pct_view)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.append({"=","&#"});
BOOST_TEST(is_equal(*it, {"%3D","%26%23"}));
// invalid pecent-escape
BOOST_TEST_THROWS(qp.append(
{"%F", no_value}), system::system_error);
};
check(f, "?", "&%3D=%26%23", { {"",no_value}, {"%3D","%26%23"} });
check(f, "?key=value", "key=value&%3D=%26%23", { {"key","value"}, {"%3D","%26%23"} });
}
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.append({"middle",qp.begin()->value});
BOOST_TEST(is_equal(*it, {"middle","John"}));
};
check(f, "?first=John&last=Doe", "first=John&last=Doe&middle=John",
{ {"first","John"}, {"last","Doe"}, {"middle","John"} });
}
// append(initializer_list)
// append(FwdIt first, FwdIt)
{
auto const f = [](params_encoded_ref qp)
{
qp.append({ {"first", nullptr}, {"last",""}, {"full", "John Doe"} });
// invalid pecent-escape
BOOST_TEST_THROWS(qp.append(
{ {"%FG", no_value} }), system::system_error);
};
auto const g = [](params_encoded_ref qp)
{
append(qp, { {"first",nullptr}, {"last",""}, {"full", "John Doe"} });
// invalid pecent-escape
BOOST_TEST_THROWS(append(qp,
{ {"%FG", no_value} }), system::system_error);
};
check(f, g, "", "first&last=&full=John%20Doe",
{ {"first",no_value}, {"last",""}, {"full","John%20Doe"} });
check(f, g, "?", "&first&last=&full=John%20Doe",
{ {"",no_value}, {"first",no_value}, {"last",""}, {"full","John%20Doe"} });
check(f, g, "?key=value", "key=value&first&last=&full=John%20Doe",
{ {"key","value"}, {"first",no_value}, {"last",""}, {"full","John%20Doe"} });
}
// insert(iterator, param_pct_view)
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.insert(std::next(qp.begin(),0),
{"middle",qp.begin()->value});
BOOST_TEST(is_equal(*it, {"middle","John"}));
// invalid pecent-escape
BOOST_TEST_THROWS(qp.insert(std::next(qp.begin(), 0),
{"", "%"}), system::system_error);
};
check(f, "?first=John&last=Doe", "middle=John&first=John&last=Doe",
{ {"middle","John"}, {"first","John"}, {"last","Doe"} });
}
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.insert(std::next(qp.begin(),1),
{"middle",qp.begin()->value});
BOOST_TEST(is_equal(*it, {"middle","John"}));
};
check(f, "?first=John&last=Doe", "first=John&middle=John&last=Doe",
{ {"first","John"}, {"middle","John"}, {"last","Doe"} });
}
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.insert(std::next(qp.begin(),2),
{"middle",qp.begin()->value});
BOOST_TEST(is_equal(*it, {"middle","John"}));
};
check(f, "?first=John&last=Doe", "first=John&last=Doe&middle=John",
{ {"first","John"}, {"last","Doe"}, {"middle","John"} });
}
// insert(iterator, initializer_list)
// insert(iterator, FwdIt first, FwdIt)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),0),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, qp.begin());
// invalid pecent-escape
BOOST_TEST_THROWS(qp.insert(std::next(qp.begin(), 0),
{ {"key", "%%"} }), system::system_error);
};
auto const g = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),0),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, qp.begin());
// invalid pecent-escape
BOOST_TEST_THROWS(insert(qp, std::next(qp.begin(), 0),
{ {"key", "%FX"} }), system::system_error);
};
check(f, g, "?k1&k2=&k3=v3",
"first=John&last=Doe&k1&k2=&k3=v3",
{ {"first","John"}, {"last","Doe"}, {"k1",no_value}, {"k2",""}, {"k3","v3"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),1),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, std::next(qp.begin(), 1));
};
auto const g = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),1),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, std::next(qp.begin(), 1));
};
check(f, g, "?k1&k2=&k3=v3",
"k1&first=John&last=Doe&k2=&k3=v3",
{ {"k1",no_value}, {"first","John"}, {"last","Doe"}, {"k2",""}, {"k3","v3"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),2),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, std::next(qp.begin(), 2));
};
auto const g = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),2),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, std::next(qp.begin(), 2));
};
check(f, g, "?k1&k2=&k3=v3",
"k1&k2=&first=John&last=Doe&k3=v3",
{ {"k1",no_value}, {"k2",""}, {"first","John"}, {"last","Doe"}, {"k3","v3"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),3),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, std::next(qp.begin(), 3));
};
auto const g = [](params_encoded_ref qp)
{
auto it = qp.insert(std::next(qp.begin(),3),
{ {"first","John"}, {"last","Doe"} });
BOOST_TEST(is_equal(*it, {"first","John"}));
BOOST_TEST_EQ(it, std::next(qp.begin(), 3));
};
check(f, g, "?k1&k2=&k3=v3",
"k1&k2=&k3=v3&first=John&last=Doe",
{ {"k1",no_value}, {"k2",""}, {"k3","v3"}, {"first","John"}, {"last","Doe"} });
}
// erase(iterator)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.erase(std::next(qp.begin(),0));
BOOST_TEST(is_equal(*it, {"last","Doe"}));
};
check(f, "?first=John&last=Doe", "last=Doe", { {"last","Doe"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.erase(std::next(qp.begin(),1));
BOOST_TEST_EQ(it, qp.end());
};
check(f, "?first=John&last=Doe", "first=John", { {"first","John"} });
}
// erase(iterator, iterator)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.erase(
std::next(qp.begin(),0),
std::next(qp.begin(),2));
BOOST_TEST(is_equal(*it, {"k2","key"}));
};
check(f, "?k0&k1=&k2=key", "k2=key", { {"k2","key"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.erase(
std::next(qp.begin(),1),
std::next(qp.begin(),3));
BOOST_TEST_EQ(it, qp.end());
};
check(f, "?k0&k1=&k2=key", "k0", { {"k0",no_value} });
}
// erase(pct_string_view, ignore_case_param)
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto n = qp.erase(qp.find_last("k1", ignore_case)->value);
BOOST_TEST_EQ(n, 2);
// invalid pecent-escape
BOOST_TEST_THROWS(qp.erase("%"), system::system_error);
};
check(f, "?k0&k1=&k2=key&k1=value&k3=4&K1=k1", "k0&k2=key&k3=4&K1=k1",
{ {"k0",no_value}, {"k2","key"}, {"k3","4"}, {"K1","k1"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto n = qp.erase("k1", ignore_case);
BOOST_TEST_EQ(n, 3);
};
check(f, "?k0&k1=&k2=key&k1=value&k3=4&K1=5", "k0&k2=key&k3=4",
{ {"k0",no_value}, {"k2","key"}, {"k3","4"} });
}
// replace(iterator, param_pct_view)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.replace(std::next(qp.begin(),0),
{"=","&#"});
BOOST_TEST(is_equal(*it, {"%3D","%26%23"}));
// invalid pecent-escape
BOOST_TEST_THROWS(qp.replace(std::next(qp.begin(), 0),
{"", "00%"}), system::system_error);
};
check(f, "?first=John&last=Doe", "%3D=%26%23&last=Doe",
{ {"%3D","%26%23"}, {"last","Doe"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.replace(std::next(qp.begin(),1),
{"=","&#"});
BOOST_TEST(is_equal(*it, {"%3D","%26%23"}));
};
check(f, "?first=John&last=Doe", "first=John&%3D=%26%23",
{ {"first","John"}, {"%3D","%26%23"} });
}
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.replace(std::next(qp.begin(),0),
*std::next(qp.begin(),1));
BOOST_TEST(is_equal(*it, {"last","Doe"}));
};
check(f, "?first=John&last=Doe", "last=Doe&last=Doe",
{ {"last","Doe"}, {"last","Doe"} });
}
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.replace(std::next(qp.begin(),1),
*std::next(qp.begin(),0));
BOOST_TEST(is_equal(*it, {"first","John"}));
};
check(f, "?first=John&last=Doe", "first=John&first=John",
{ {"first","John"}, {"first","John"} });
}
// replace(iterator, iterator, initializer_list)
// replace(iterator, iterator, FwdIt, FwdIt)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.replace(
std::next(qp.begin(),0),
std::next(qp.begin(),2),
{{"%3D","%26%23"}});
BOOST_TEST(is_equal(*it, {"%3D","%26%23"}));
// invalid pecent-escape
BOOST_TEST_THROWS(qp.replace(
std::next(qp.begin(),0),
std::next(qp.begin(),2),
{ {"%", "%"} }), system::system_error);
};
auto const g = [](params_encoded_ref qp)
{
auto it = replace(qp,
std::next(qp.begin(),0),
std::next(qp.begin(),2),
{{"%3D","%26%23"}});
BOOST_TEST(is_equal(*it, {"%3D","%26%23"}));
// invalid pecent-escape
BOOST_TEST_THROWS(replace(qp,
std::next(qp.begin(),0),
std::next(qp.begin(),2),
{ {"%", "%"} }), system::system_error);
};
check(f, g, "?k0&k1=&k2=key", "%3D=%26%23&k2=key",
{ {"%3D","%26%23"}, {"k2","key"} });
}
// unset(iterator)
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.unset(std::next(qp.begin(),0));
BOOST_TEST(is_equal(*it, {"k0", no_value}));
};
check(f, "?k0&k1=&k2=key", "k0&k1=&k2=key",
{ {"k0",no_value}, {"k1",""}, {"k2","key"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.unset(std::next(qp.begin(),1));
BOOST_TEST(is_equal(*it, {"k1", no_value}));
};
check(f, "?k0&k1=&k2=key", "k0&k1&k2=key",
{ {"k0",no_value}, {"k1",no_value}, {"k2","key"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.unset(std::next(qp.begin(),2));
BOOST_TEST(is_equal(*it, {"k2", no_value}));
};
check(f, "?k0&k1=&k2=key", "k0&k1=&k2",
{ {"k0",no_value}, {"k1",""}, {"k2",no_value} });
}
// set(iterator, pct_string_view)
{
auto const f = [](params_encoded_ref qp)
{
// self-intersect
auto it = qp.set(std::next(qp.begin(),0),
qp.find("k2")->value);
BOOST_TEST(is_equal(*it, {"k0", "key"}));
// invalid pecent-escape
BOOST_TEST_THROWS(qp.set(std::next(qp.begin(),0),
"%"), system::system_error);
};
check(f, "?k0&k1=&k2=key", "k0=key&k1=&k2=key",
{ {"k0","key"}, {"k1",""}, {"k2","key"} });
}
{
auto const f = [](params_encoded_ref qp)
{
auto it = qp.set(std::next(qp.begin(),1), "%26%23");
BOOST_TEST(is_equal(*it, {"k1", "%26%23"}));
};
check(f, "?k0&k1=&k2=key", "k0&k1=%26%23&k2=key",
{ {"k0",no_value}, {"k1","%26%23"}, {"k2","key"} });
}
}
void
testJavadocs()
{
// url()
{
url u( "?key=value" );
assert( &u.encoded_params().url() == &u );
}
// assign(initializer_list)
{
url u;
u.encoded_params().assign({ { "first", "John" }, { "last", "Doe" } });
}
// append(param_pct_view)
{
url u;
u.encoded_params().append( { "first", "John" } );
}
// append(initializer_list)
{
url u;
u.encoded_params().append({ {"first", "John"}, {"last", "Doe"} });
}
// erase(iterator)
{
url u( "?first=John&last=Doe" );
params_encoded_ref::iterator it = u.encoded_params().erase( u.encoded_params().begin() );
assert( u.encoded_query() == "last=Doe" );
ignore_unused(it);
}
// replace(iterator, param_pct_view)
{
url u( "?first=John&last=Doe" );
u.encoded_params().replace( u.encoded_params().begin(), {"title", "Mr"});
assert( u.encoded_query() == "title=Mr&last=Doe" );
}
// unset(iterator)
{
url u( "?first=John&last=Doe" );
u.encoded_params().unset( u.encoded_params().begin() );
assert( u.encoded_query() == "first&last=Doe" );
}
// set(iterator, pct_string_view)
{
url u( "?id=42&id=69" );
u.encoded_params().set( u.encoded_params().begin(), "none" );
assert( u.encoded_query() == "id=none&id=69" );
}
// set(pct_string_view, pct_string_view)
{
url u( "?id=42&id=69" );
u.encoded_params().set( "id", "none" );
assert( u.encoded_params().count( "id" ) == 1 );
}
}
void
run()
{
testSpecial();
testObservers();
testModifiers();
testJavadocs();
}
};
TEST_SUITE(
params_encoded_ref_test,
"boost.url.params_encoded_ref");
} // urls
} // boost