mirror of https://github.com/JAJames/jessilib.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
9.2 KiB
238 lines
9.2 KiB
/**
|
|
* Copyright (C) 2021 Jessica James.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Written by Jessica James <jessica.aj@outlook.com>
|
|
*/
|
|
|
|
#include "jessilib/http_query.hpp"
|
|
#include <charconv>
|
|
#include "test.hpp"
|
|
|
|
using namespace std::literals;
|
|
|
|
// Compile-time tests for constexpr on compilers which support C++20 constexpr std::string
|
|
#ifdef __cpp_lib_constexpr_string
|
|
constexpr std::string query_constexpr(std::string_view in_expression) {
|
|
std::string result{ static_cast<std::string>(in_expression) };
|
|
jessilib::deserialize_http_query(result);
|
|
return result;
|
|
}
|
|
static_assert(query_constexpr("test"s) == "test"s);
|
|
static_assert(query_constexpr("first+second"s) == "first second"s);
|
|
static_assert(query_constexpr("first%20second"s) == "first second"s);
|
|
#endif // __cpp_lib_constexpr_string
|
|
|
|
using char_types = ::testing::Types<char, char8_t, char16_t, char32_t>;
|
|
using utf8_char_types = ::testing::Types<char, char8_t>;
|
|
|
|
template<typename T>
|
|
class QuerySequenceTest : public ::testing::Test {
|
|
public:
|
|
};
|
|
TYPED_TEST_SUITE(QuerySequenceTest, utf8_char_types);
|
|
|
|
constexpr char32_t MAX_LOOP_CODEPOINT = 0x100FF; // use 0x10FFFF for full testing
|
|
|
|
TYPED_TEST(QuerySequenceTest, single_chars) {
|
|
// [U+0000, U+100FF)
|
|
for (char32_t codepoint = 0; codepoint < MAX_LOOP_CODEPOINT; ++codepoint) {
|
|
std::basic_string<TypeParam> expected;
|
|
size_t units = jessilib::encode_codepoint(expected, codepoint);
|
|
EXPECT_NE(units, 0);
|
|
EXPECT_EQ(units, expected.size());
|
|
|
|
// Construct the query string
|
|
std::basic_string<TypeParam> query_string;
|
|
for (auto& unit : expected) {
|
|
char encoded[3] { '%', 0, 0 };
|
|
char* encoded_end = encoded + sizeof(encoded);
|
|
auto to_chars_result = std::to_chars(encoded + 1, encoded_end, static_cast<unsigned char>(unit), 16);
|
|
ASSERT_EQ(to_chars_result.ec, std::errc{}) // assertion will fail when `unit` is signed type
|
|
<< "For unit " << static_cast<int>(unit) << " in codepoint " << static_cast<int>(codepoint) << std::endl;
|
|
|
|
if (to_chars_result.ptr != encoded_end) {
|
|
// Only wrote one hex; shift it
|
|
encoded[2] = encoded[1];
|
|
encoded[1] = '0';
|
|
}
|
|
|
|
EXPECT_EQ(encoded[0], '%');
|
|
EXPECT_NE(encoded[1], 0);
|
|
EXPECT_NE(encoded[2], 0);
|
|
query_string.insert(query_string.end(), encoded, encoded_end);
|
|
}
|
|
EXPECT_EQ(query_string.size(), expected.size() * 3);
|
|
|
|
// Decode & check the query string
|
|
jessilib::deserialize_http_query(query_string);
|
|
EXPECT_EQ(query_string, expected);
|
|
}
|
|
}
|
|
|
|
TYPED_TEST(QuerySequenceTest, invalids) {
|
|
std::basic_string<TypeParam> query_string, long_query_string;
|
|
for (size_t unit = 0; unit <= 0xFF; ++unit) {
|
|
TypeParam encoded[2] { '%', static_cast<TypeParam>(unit) };
|
|
TypeParam* encoded_end = encoded + sizeof(encoded);
|
|
query_string.insert(query_string.end(), encoded, encoded_end);
|
|
|
|
long_query_string += query_string;
|
|
jessilib::deserialize_http_query(query_string);
|
|
EXPECT_TRUE(query_string.empty())
|
|
<< "in unit: " << unit << std::endl;
|
|
}
|
|
|
|
jessilib::deserialize_http_query(long_query_string);
|
|
EXPECT_TRUE(long_query_string.empty());
|
|
}
|
|
|
|
TYPED_TEST(QuerySequenceTest, invalids_2len) {
|
|
std::basic_string<TypeParam> query_string, long_query_string;
|
|
for (size_t unit = 0; unit <= 0xFFFF; ++unit) {
|
|
TypeParam first = static_cast<TypeParam>(unit >> 8); // order of these two doesn't matter
|
|
TypeParam second = static_cast<TypeParam>(unit & 0xFF);
|
|
if (jessilib::as_base(first, 16) >= 0
|
|
&& jessilib::as_base(second, 16) >= 0) {
|
|
continue;
|
|
}
|
|
TypeParam encoded[3] { '%', static_cast<TypeParam>(first), static_cast<TypeParam>(second) };
|
|
TypeParam* encoded_end = encoded + sizeof(encoded);
|
|
query_string.insert(query_string.end(), encoded, encoded_end);
|
|
|
|
long_query_string += query_string;
|
|
jessilib::deserialize_http_query(query_string);
|
|
EXPECT_TRUE(query_string.empty())
|
|
<< "in unit: " << unit << std::endl;
|
|
}
|
|
|
|
jessilib::deserialize_http_query(long_query_string);
|
|
EXPECT_TRUE(long_query_string.empty());
|
|
}
|
|
|
|
TYPED_TEST(QuerySequenceTest, invalids_trailing) {
|
|
std::basic_string<TypeParam> query_string, long_query_string;
|
|
for (size_t unit = 0; unit <= 0xFF; ++unit) {
|
|
TypeParam encoded[3] { '%', static_cast<TypeParam>(unit), '%' };
|
|
TypeParam* encoded_end = encoded + sizeof(encoded);
|
|
query_string.insert(query_string.end(), encoded, encoded_end);
|
|
|
|
long_query_string += query_string;
|
|
jessilib::deserialize_http_query(query_string);
|
|
EXPECT_TRUE(query_string.empty())
|
|
<< "in unit: " << unit << std::endl;
|
|
}
|
|
|
|
jessilib::deserialize_http_query(long_query_string);
|
|
EXPECT_TRUE(long_query_string.empty());
|
|
}
|
|
|
|
TYPED_TEST(QuerySequenceTest, invalids_2len_trailing) {
|
|
std::basic_string<TypeParam> query_string, long_query_string;
|
|
for (size_t unit = 0; unit <= 0xFFFF; ++unit) {
|
|
TypeParam first = static_cast<TypeParam>(unit >> 8); // order of these two doesn't matter
|
|
TypeParam second = static_cast<TypeParam>(unit & 0xFF);
|
|
if (jessilib::as_base(first, 16) >= 0
|
|
&& jessilib::as_base(second, 16) >= 0) {
|
|
continue;
|
|
}
|
|
TypeParam encoded[4] { '%', static_cast<TypeParam>(first), static_cast<TypeParam>(second), '%' };
|
|
TypeParam* encoded_end = encoded + sizeof(encoded);
|
|
query_string.insert(query_string.end(), encoded, encoded_end);
|
|
|
|
long_query_string += query_string;
|
|
jessilib::deserialize_http_query(query_string);
|
|
EXPECT_TRUE(query_string.empty())
|
|
<< "in unit: " << unit << std::endl;
|
|
}
|
|
|
|
jessilib::deserialize_http_query(long_query_string);
|
|
EXPECT_TRUE(long_query_string.empty());
|
|
}
|
|
|
|
TEST(HtmlFormParser, empty) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text;
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_TRUE(query_text.empty());
|
|
EXPECT_TRUE(parsed_result.empty());
|
|
}
|
|
|
|
TEST(HtmlFormParser, one_key) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text = "key";
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_EQ(query_text, "key");
|
|
EXPECT_EQ(parsed_result.size(), 1);
|
|
EXPECT_EQ(parsed_result[0].first, query_text);
|
|
EXPECT_TRUE(parsed_result[0].second.empty());
|
|
}
|
|
|
|
TEST(HtmlFormParser, one_key_and_value) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text = "key=value";
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_TRUE(query_text.starts_with("keyvalue"));
|
|
EXPECT_EQ(parsed_result.size(), 1);
|
|
EXPECT_EQ(parsed_result[0].first, "key");
|
|
EXPECT_EQ(parsed_result[0].second, "value");
|
|
}
|
|
|
|
TEST(HtmlFormParser, one_key_and_value_trailing) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text = "key=value&";
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_TRUE(query_text.starts_with("keyvalue"));
|
|
EXPECT_EQ(parsed_result.size(), 2);
|
|
EXPECT_EQ(parsed_result[0].first, "key");
|
|
EXPECT_EQ(parsed_result[0].second, "value");
|
|
EXPECT_TRUE(parsed_result[1].first.empty());
|
|
EXPECT_TRUE(parsed_result[1].second.empty());
|
|
}
|
|
|
|
TEST(HtmlFormParser, two_key_one_value) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text = "key=value&second_key";
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_TRUE(query_text.starts_with("keyvaluesecond_key"));
|
|
EXPECT_EQ(parsed_result.size(), 2);
|
|
EXPECT_EQ(parsed_result[0].first, "key");
|
|
EXPECT_EQ(parsed_result[0].second, "value");
|
|
EXPECT_EQ(parsed_result[1].first, "second_key");
|
|
EXPECT_TRUE(parsed_result[1].second.empty());
|
|
}
|
|
|
|
TEST(HtmlFormParser, two_key_two_value) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text = "key=value&second_key=second=value";
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_TRUE(query_text.starts_with("keyvaluesecond_keysecond=value"));
|
|
EXPECT_EQ(parsed_result.size(), 2);
|
|
EXPECT_EQ(parsed_result[0].first, "key");
|
|
EXPECT_EQ(parsed_result[0].second, "value");
|
|
EXPECT_EQ(parsed_result[1].first, "second_key");
|
|
EXPECT_EQ(parsed_result[1].second, "second=value");
|
|
}
|
|
|
|
TEST(HtmlFormParser, some_sequences) {
|
|
std::vector<std::pair<std::string_view, std::string_view>> parsed_result;
|
|
std::string query_text = "k+y=va+u%20&%73econd%5Fke%79=second_valu%65";
|
|
EXPECT_TRUE(jessilib::deserialize_html_form(parsed_result, query_text));
|
|
EXPECT_TRUE(query_text.starts_with("k yva u second_keysecond_value"));
|
|
EXPECT_EQ(parsed_result.size(), 2);
|
|
EXPECT_EQ(parsed_result[0].first, "k y");
|
|
EXPECT_EQ(parsed_result[0].second, "va u ");
|
|
EXPECT_EQ(parsed_result[1].first, "second_key");
|
|
EXPECT_EQ(parsed_result[1].second, "second_value");
|
|
}
|
|
|