297 lines
9.0 KiB
C++
297 lines
9.0 KiB
C++
|
// NOLINT(llvm-header-guard)
|
||
|
|
||
|
#define PBF_TYPE_NAME PROTOZERO_TEST_STRING(PBF_TYPE)
|
||
|
#define GET_TYPE PROTOZERO_TEST_CONCAT(get_packed_, PBF_TYPE)
|
||
|
#define ADD_TYPE PROTOZERO_TEST_CONCAT(add_packed_, PBF_TYPE)
|
||
|
|
||
|
using packed_field_type = PROTOZERO_TEST_CONCAT(protozero::packed_field_, PBF_TYPE);
|
||
|
|
||
|
TEST_CASE("read repeated packed field: " PBF_TYPE_NAME) {
|
||
|
// Run these tests twice, the second time we basically move the data
|
||
|
// one byte down in the buffer. It doesn't matter how the data or buffer
|
||
|
// is aligned before that, in at least one of these cases the ints will
|
||
|
// not be aligned properly. So we test that even in that case the ints
|
||
|
// will be extracted properly.
|
||
|
|
||
|
for (std::string::size_type n = 0; n < 2; ++n) {
|
||
|
std::string abuffer;
|
||
|
abuffer.reserve(1000);
|
||
|
abuffer.append(n, '\0');
|
||
|
|
||
|
SECTION("empty") {
|
||
|
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
|
||
|
|
||
|
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||
|
|
||
|
REQUIRE_FALSE(item.next());
|
||
|
}
|
||
|
|
||
|
SECTION("one") {
|
||
|
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||
|
|
||
|
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||
|
|
||
|
REQUIRE(item.next());
|
||
|
const auto it_range = item.GET_TYPE();
|
||
|
REQUIRE_FALSE(item.next());
|
||
|
|
||
|
REQUIRE(it_range.begin() != it_range.end());
|
||
|
REQUIRE(*it_range.begin() == 17);
|
||
|
REQUIRE(std::next(it_range.begin()) == it_range.end());
|
||
|
}
|
||
|
|
||
|
SECTION("many") {
|
||
|
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||
|
|
||
|
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||
|
|
||
|
REQUIRE(item.next());
|
||
|
const auto it_range = item.GET_TYPE();
|
||
|
REQUIRE_FALSE(item.next());
|
||
|
|
||
|
auto it = it_range.begin();
|
||
|
REQUIRE(it != it_range.end());
|
||
|
REQUIRE(*it++ == 17);
|
||
|
REQUIRE(*it++ == 200);
|
||
|
REQUIRE(*it++ == 0);
|
||
|
REQUIRE(*it++ == 1);
|
||
|
REQUIRE(*it++ == std::numeric_limits<cpp_type>::max());
|
||
|
#if PBF_TYPE_IS_SIGNED
|
||
|
REQUIRE(*it++ == -200);
|
||
|
REQUIRE(*it++ == -1);
|
||
|
REQUIRE(*it++ == std::numeric_limits<cpp_type>::min());
|
||
|
#endif
|
||
|
REQUIRE(it == it_range.end());
|
||
|
}
|
||
|
|
||
|
SECTION("swap iterator range") {
|
||
|
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||
|
|
||
|
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||
|
|
||
|
REQUIRE(item.next());
|
||
|
auto it_range1 = item.GET_TYPE();
|
||
|
REQUIRE_FALSE(item.next());
|
||
|
|
||
|
decltype(it_range1) it_range;
|
||
|
using std::swap;
|
||
|
swap(it_range, it_range1);
|
||
|
|
||
|
auto it = it_range.begin();
|
||
|
REQUIRE(it != it_range.end());
|
||
|
REQUIRE(*it++ == 17);
|
||
|
REQUIRE(*it++ == 200);
|
||
|
REQUIRE(*it++ == 0);
|
||
|
REQUIRE(*it++ == 1);
|
||
|
REQUIRE(*it++ == std::numeric_limits<cpp_type>::max());
|
||
|
}
|
||
|
|
||
|
SECTION("end_of_buffer") {
|
||
|
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||
|
|
||
|
for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
|
||
|
protozero::pbf_reader item{abuffer.data() + n, i};
|
||
|
REQUIRE(item.next());
|
||
|
REQUIRE_THROWS_AS(item.GET_TYPE(), const protozero::end_of_buffer_exception&);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
TEST_CASE("write repeated packed field: " PBF_TYPE_NAME) {
|
||
|
std::string buffer;
|
||
|
protozero::pbf_writer pw{buffer};
|
||
|
|
||
|
SECTION("empty") {
|
||
|
cpp_type data[] = { 17 };
|
||
|
pw.ADD_TYPE(1, std::begin(data), std::begin(data) /* !!!! */);
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
|
||
|
}
|
||
|
|
||
|
SECTION("one") {
|
||
|
cpp_type data[] = { 17 };
|
||
|
pw.ADD_TYPE(1, std::begin(data), std::end(data));
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||
|
}
|
||
|
|
||
|
SECTION("many") {
|
||
|
cpp_type data[] = {
|
||
|
17
|
||
|
, 200
|
||
|
, 0
|
||
|
, 1
|
||
|
,std::numeric_limits<cpp_type>::max()
|
||
|
#if PBF_TYPE_IS_SIGNED
|
||
|
,-200
|
||
|
, -1
|
||
|
,std::numeric_limits<cpp_type>::min()
|
||
|
#endif
|
||
|
};
|
||
|
pw.ADD_TYPE(1, std::begin(data), std::end(data));
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
TEST_CASE("write repeated packed field using packed field: " PBF_TYPE_NAME) {
|
||
|
std::string buffer;
|
||
|
protozero::pbf_writer pw{buffer};
|
||
|
|
||
|
SECTION("empty - should do rollback") {
|
||
|
{
|
||
|
packed_field_type field{pw, 1};
|
||
|
}
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
|
||
|
}
|
||
|
|
||
|
SECTION("one") {
|
||
|
{
|
||
|
packed_field_type field{pw, 1};
|
||
|
field.add_element(cpp_type(17));
|
||
|
}
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||
|
}
|
||
|
|
||
|
SECTION("many") {
|
||
|
{
|
||
|
packed_field_type field{pw, 1};
|
||
|
field.add_element(cpp_type( 17));
|
||
|
field.add_element(cpp_type( 200));
|
||
|
field.add_element(cpp_type( 0));
|
||
|
field.add_element(cpp_type( 1));
|
||
|
field.add_element(std::numeric_limits<cpp_type>::max());
|
||
|
#if PBF_TYPE_IS_SIGNED
|
||
|
field.add_element(cpp_type(-200));
|
||
|
field.add_element(cpp_type( -1));
|
||
|
field.add_element(std::numeric_limits<cpp_type>::min());
|
||
|
#endif
|
||
|
REQUIRE(field.valid());
|
||
|
SECTION("with commit") {
|
||
|
field.commit();
|
||
|
REQUIRE_FALSE(field.valid());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TEST_CASE("move repeated packed field: " PBF_TYPE_NAME) {
|
||
|
std::string buffer;
|
||
|
protozero::pbf_writer pw{buffer};
|
||
|
|
||
|
SECTION("move rvalue") {
|
||
|
packed_field_type field;
|
||
|
REQUIRE_FALSE(field.valid());
|
||
|
field = packed_field_type{pw, 1};
|
||
|
REQUIRE(field.valid());
|
||
|
field.add_element(cpp_type(17));
|
||
|
}
|
||
|
|
||
|
SECTION("explicit move") {
|
||
|
packed_field_type field2{pw, 1};
|
||
|
packed_field_type field;
|
||
|
|
||
|
REQUIRE(field2.valid());
|
||
|
REQUIRE_FALSE(field.valid());
|
||
|
|
||
|
field = std::move(field2);
|
||
|
|
||
|
REQUIRE_FALSE(field2.valid()); // NOLINT(hicpp-invalid-access-moved, bugprone-use-after-move)
|
||
|
REQUIRE(field.valid());
|
||
|
|
||
|
field.add_element(cpp_type(17));
|
||
|
}
|
||
|
|
||
|
SECTION("move constructor") {
|
||
|
packed_field_type field2{pw, 1};
|
||
|
REQUIRE(field2.valid());
|
||
|
|
||
|
packed_field_type field{std::move(field2)};
|
||
|
REQUIRE(field.valid());
|
||
|
REQUIRE_FALSE(field2.valid()); // NOLINT(hicpp-invalid-access-moved, bugprone-use-after-move)
|
||
|
|
||
|
field.add_element(cpp_type(17));
|
||
|
}
|
||
|
|
||
|
SECTION("swap") {
|
||
|
packed_field_type field;
|
||
|
packed_field_type field2{pw, 1};
|
||
|
|
||
|
REQUIRE_FALSE(field.valid());
|
||
|
REQUIRE(field2.valid());
|
||
|
|
||
|
using std::swap;
|
||
|
swap(field, field2);
|
||
|
|
||
|
REQUIRE(field.valid());
|
||
|
REQUIRE_FALSE(field2.valid());
|
||
|
|
||
|
field.add_element(cpp_type(17));
|
||
|
}
|
||
|
|
||
|
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||
|
}
|
||
|
|
||
|
TEST_CASE("write from different types of iterators: " PBF_TYPE_NAME) {
|
||
|
std::string buffer;
|
||
|
protozero::pbf_writer pw{buffer};
|
||
|
|
||
|
SECTION("from uint16_t") {
|
||
|
#if PBF_TYPE_IS_SIGNED
|
||
|
const int16_t data[] = { 1, 4, 9, 16, 25 };
|
||
|
#else
|
||
|
const uint16_t data[] = { 1, 4, 9, 16, 25 };
|
||
|
#endif
|
||
|
|
||
|
pw.ADD_TYPE(1, std::begin(data), std::end(data));
|
||
|
}
|
||
|
|
||
|
SECTION("from string") {
|
||
|
std::string data{"1 4 9 16 25"};
|
||
|
std::stringstream sdata{data};
|
||
|
|
||
|
#if PBF_TYPE_IS_SIGNED
|
||
|
using test_type = int32_t;
|
||
|
#else
|
||
|
using test_type = uint32_t;
|
||
|
#endif
|
||
|
|
||
|
std::istream_iterator<test_type> eod;
|
||
|
std::istream_iterator<test_type> it(sdata);
|
||
|
|
||
|
pw.ADD_TYPE(1, it, eod);
|
||
|
}
|
||
|
|
||
|
protozero::pbf_reader item{buffer};
|
||
|
|
||
|
REQUIRE(item.next());
|
||
|
auto it_range = item.GET_TYPE();
|
||
|
REQUIRE_FALSE(item.next());
|
||
|
REQUIRE_FALSE(it_range.empty());
|
||
|
REQUIRE(std::distance(it_range.begin(), it_range.end()) == 5);
|
||
|
REQUIRE(it_range.size() == 5);
|
||
|
|
||
|
REQUIRE(it_range.front() == 1); it_range.drop_front();
|
||
|
REQUIRE(it_range.front() == 4); it_range.drop_front();
|
||
|
REQUIRE(std::distance(it_range.begin(), it_range.end()) == 3);
|
||
|
REQUIRE(it_range.size() == 3);
|
||
|
REQUIRE(it_range.front() == 9); it_range.drop_front();
|
||
|
REQUIRE(it_range.front() == 16); it_range.drop_front();
|
||
|
REQUIRE(it_range.front() == 25); it_range.drop_front();
|
||
|
REQUIRE(it_range.empty());
|
||
|
REQUIRE(std::distance(it_range.begin(), it_range.end()) == 0);
|
||
|
REQUIRE(it_range.size() == 0); // NOLINT(readability-container-size-empty)
|
||
|
|
||
|
REQUIRE_THROWS_AS(it_range.front(), const assert_error&);
|
||
|
REQUIRE_THROWS_AS(it_range.drop_front(), const assert_error&);
|
||
|
}
|
||
|
|