// 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::max()); #if PBF_TYPE_IS_SIGNED REQUIRE(*it++ == -200); REQUIRE(*it++ == -1); REQUIRE(*it++ == std::numeric_limits::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::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::max() #if PBF_TYPE_IS_SIGNED ,-200 , -1 ,std::numeric_limits::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::max()); #if PBF_TYPE_IS_SIGNED field.add_element(cpp_type(-200)); field.add_element(cpp_type( -1)); field.add_element(std::numeric_limits::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 eod; std::istream_iterator 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&); }