// Copyright 2022 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include #include #include #include #include "gtest/gtest.h" #include "pw_bytes/span.h" #include "pw_containers/vector.h" #include "pw_status/status.h" #include "pw_status/status_with_size.h" #include "pw_stream/memory_stream.h" // These header files contain the code generated by the pw_protobuf plugin. // They are re-generated every time the tests are built and are used by the // tests to ensure that the interface remains consistent. // // The purpose of the tests in this file is primarily to verify that the // generated C++ interface is valid rather than the correctness of the // low-level encoder. #include "pw_protobuf_test_protos/full_test.pwpb.h" #include "pw_protobuf_test_protos/importer.pwpb.h" #include "pw_protobuf_test_protos/non_pw_package.pwpb.h" #include "pw_protobuf_test_protos/proto2.pwpb.h" #include "pw_protobuf_test_protos/repeated.pwpb.h" namespace pw::protobuf { namespace { using namespace pw::protobuf::test; TEST(Codegen, StreamDecoder) { // clang-format off constexpr uint8_t proto_data[] = { // pigweed.magic_number 0x08, 0x49, // pigweed.ziggy 0x10, 0xdd, 0x01, // pigweed.error_message 0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ', 't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r', // pigweed.bin 0x40, 0x01, // pigweed.pigweed 0x3a, 0x02, // pigweed.pigweed.status 0x08, 0x02, // pigweed.proto 0x4a, 0x56, // pigweed.proto.bin 0x10, 0x00, // pigweed.proto.pigweed_pigweed_bin 0x18, 0x00, // pigweed.proto.pigweed_protobuf_bin 0x20, 0x01, // pigweed.proto.meta 0x2a, 0x0f, // pigweed.proto.meta.file_name 0x0a, 0x0b, '/', 'e', 't', 'c', '/', 'p', 'a', 's', 's', 'w', 'd', // pigweed.proto.meta.status 0x10, 0x02, // pigweed.proto.nested_pigweed 0x0a, 0x3d, // pigweed.proto.nested_pigweed.error_message 0x2a, 0x10, 'h', 'e', 'r', 'e', ' ', 'w', 'e', ' ', 'g', 'o', ' ', 'a', 'g', 'a', 'i', 'n', // pigweed.proto.nested_pigweed.magic_number 0x08, 0xe8, 0x04, // pigweed.proto.nested_pigweed.device_info 0x32, 0x26, // pigweed.proto.nested_pigweed.device_info.attributes[0] 0x22, 0x10, // pigweed.proto.nested_pigweed.device_info.attributes[0].key 0x0a, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // pigweed.proto.nested_pigweed.device_info.attributes[0].value 0x12, 0x05, '5', '.', '3', '.', '1', // pigweed.proto.nested_pigweed.device_info.attributes[1] 0x22, 0x10, // pigweed.proto.nested_pigweed.device_info.attributes[1].key 0x0a, 0x04, 'c', 'h', 'i', 'p', // pigweed.proto.nested_pigweed.device_info.attributes[1].value 0x12, 0x08, 'l', 'e', 'f', 't', '-', 's', 'o', 'c', // pigweed.proto.nested_pigweed.device_info.status 0x18, 0x03, // pigweed.id[0] 0x52, 0x02, // pigweed.id[0].id 0x08, 0x31, // pigweed.id[1] 0x52, 0x02, // pigweed.id[1].id 0x08, 0x39, // pigweed.id[2] 0x52, 0x02, // pigweed.id[2].id 0x08, 0x4b, // pigweed.id[3] 0x52, 0x02, // pigweed.id[3].id 0x08, 0x67, // pigweed.id[4] 0x52, 0x03, // pigweed.id[4].id 0x08, 0x8d, 0x01 }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); Pigweed::StreamDecoder pigweed(reader); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::MAGIC_NUMBER); Result magic_number = pigweed.ReadMagicNumber(); EXPECT_EQ(magic_number.status(), OkStatus()); EXPECT_EQ(magic_number.value(), 0x49u); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ZIGGY); Result ziggy = pigweed.ReadZiggy(); EXPECT_EQ(ziggy.status(), OkStatus()); EXPECT_EQ(ziggy.value(), -111); constexpr std::string_view kExpectedErrorMessage{"not a typewriter"}; EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE); std::array error_message{}; StatusWithSize error_message_status = pigweed.ReadErrorMessage(error_message); EXPECT_EQ(error_message_status.status(), OkStatus()); EXPECT_EQ(error_message_status.size(), kExpectedErrorMessage.size()); EXPECT_EQ(std::memcmp(error_message.data(), kExpectedErrorMessage.data(), kExpectedErrorMessage.size()), 0); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::BIN); Result bin = pigweed.ReadBin(); EXPECT_EQ(bin.status(), OkStatus()); EXPECT_EQ(bin.value(), Pigweed::Protobuf::Binary::ZERO); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::PIGWEED); { Pigweed::Pigweed::StreamDecoder pigweed_pigweed = pigweed.GetPigweedDecoder(); EXPECT_EQ(pigweed_pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed_pigweed.Field().value(), Pigweed::Pigweed::Fields::STATUS); Result pigweed_status = pigweed_pigweed.ReadStatus(); EXPECT_EQ(pigweed_status.status(), OkStatus()); EXPECT_EQ(pigweed_status.value(), Bool::FILE_NOT_FOUND); EXPECT_EQ(pigweed_pigweed.Next(), Status::OutOfRange()); } EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::PROTO); { Proto::StreamDecoder proto = pigweed.GetProtoDecoder(); EXPECT_EQ(proto.Next(), OkStatus()); EXPECT_EQ(proto.Field().value(), Proto::Fields::BIN); Result proto_bin = proto.ReadBin(); EXPECT_EQ(proto_bin.status(), OkStatus()); EXPECT_EQ(proto_bin.value(), Proto::Binary::OFF); EXPECT_EQ(proto.Next(), OkStatus()); EXPECT_EQ(proto.Field().value(), Proto::Fields::PIGWEED_PIGWEED_BIN); Result proto_pigweed_bin = proto.ReadPigweedPigweedBin(); EXPECT_EQ(proto_pigweed_bin.status(), OkStatus()); EXPECT_EQ(proto_pigweed_bin.value(), Pigweed::Pigweed::Binary::ZERO); EXPECT_EQ(proto.Next(), OkStatus()); EXPECT_EQ(proto.Field().value(), Proto::Fields::PIGWEED_PROTOBUF_BIN); Result proto_protobuf_bin = proto.ReadPigweedProtobufBin(); EXPECT_EQ(proto_protobuf_bin.status(), OkStatus()); EXPECT_EQ(proto_protobuf_bin.value(), Pigweed::Protobuf::Binary::ZERO); EXPECT_EQ(proto.Next(), OkStatus()); EXPECT_EQ(proto.Field().value(), Proto::Fields::META); { Pigweed::Protobuf::Compiler::StreamDecoder meta = proto.GetMetaDecoder(); constexpr std::string_view kExpectedFileName{"/etc/passwd"}; EXPECT_EQ(meta.Next(), OkStatus()); EXPECT_EQ(meta.Field().value(), Pigweed::Protobuf::Compiler::Fields::FILE_NAME); std::array meta_file_name{}; StatusWithSize meta_file_name_status = meta.ReadFileName(meta_file_name); EXPECT_EQ(meta_file_name_status.status(), OkStatus()); EXPECT_EQ(meta_file_name_status.size(), kExpectedFileName.size()); EXPECT_EQ(std::memcmp(meta_file_name.data(), kExpectedFileName.data(), kExpectedFileName.size()), 0); EXPECT_EQ(meta.Next(), OkStatus()); EXPECT_EQ(meta.Field().value(), Pigweed::Protobuf::Compiler::Fields::STATUS); Result meta_status = meta.ReadStatus(); EXPECT_EQ(meta_status.status(), OkStatus()); EXPECT_EQ(meta_status.value(), Pigweed::Protobuf::Compiler::Status::FUBAR); EXPECT_EQ(meta.Next(), Status::OutOfRange()); } EXPECT_EQ(proto.Next(), OkStatus()); EXPECT_EQ(proto.Field().value(), Proto::Fields::PIGWEED); { Pigweed::StreamDecoder proto_pigweed = proto.GetPigweedDecoder(); constexpr std::string_view kExpectedProtoErrorMessage{"here we go again"}; EXPECT_EQ(proto_pigweed.Next(), OkStatus()); EXPECT_EQ(proto_pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE); std::array proto_pigweed_error_message{}; StatusWithSize proto_pigweed_error_message_status = proto_pigweed.ReadErrorMessage(proto_pigweed_error_message); EXPECT_EQ(proto_pigweed_error_message_status.status(), OkStatus()); EXPECT_EQ(proto_pigweed_error_message_status.size(), kExpectedProtoErrorMessage.size()); EXPECT_EQ(std::memcmp(proto_pigweed_error_message.data(), kExpectedProtoErrorMessage.data(), kExpectedProtoErrorMessage.size()), 0); EXPECT_EQ(proto_pigweed.Next(), OkStatus()); EXPECT_EQ(proto_pigweed.Field().value(), Pigweed::Fields::MAGIC_NUMBER); Result proto_pigweed_magic_number = proto_pigweed.ReadMagicNumber(); EXPECT_EQ(proto_pigweed_magic_number.status(), OkStatus()); EXPECT_EQ(proto_pigweed_magic_number.value(), 616u); EXPECT_EQ(proto_pigweed.Next(), OkStatus()); EXPECT_EQ(proto_pigweed.Field().value(), Pigweed::Fields::DEVICE_INFO); { DeviceInfo::StreamDecoder device_info = proto_pigweed.GetDeviceInfoDecoder(); EXPECT_EQ(device_info.Next(), OkStatus()); EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::ATTRIBUTES); { KeyValuePair::StreamDecoder key_value_pair = device_info.GetAttributesDecoder(); constexpr std::string_view kExpectedKey{"version"}; constexpr std::string_view kExpectedValue{"5.3.1"}; EXPECT_EQ(key_value_pair.Next(), OkStatus()); EXPECT_EQ(key_value_pair.Field().value(), KeyValuePair::Fields::KEY); std::array key{}; StatusWithSize key_status = key_value_pair.ReadKey(key); EXPECT_EQ(key_status.status(), OkStatus()); EXPECT_EQ(key_status.size(), kExpectedKey.size()); EXPECT_EQ( std::memcmp(key.data(), kExpectedKey.data(), kExpectedKey.size()), 0); EXPECT_EQ(key_value_pair.Next(), OkStatus()); EXPECT_EQ(key_value_pair.Field().value(), KeyValuePair::Fields::VALUE); std::array value{}; StatusWithSize value_status = key_value_pair.ReadValue(value); EXPECT_EQ(value_status.status(), OkStatus()); EXPECT_EQ(value_status.size(), kExpectedValue.size()); EXPECT_EQ( std::memcmp( value.data(), kExpectedValue.data(), kExpectedValue.size()), 0); EXPECT_EQ(key_value_pair.Next(), Status::OutOfRange()); } EXPECT_EQ(device_info.Next(), OkStatus()); EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::ATTRIBUTES); { KeyValuePair::StreamDecoder key_value_pair = device_info.GetAttributesDecoder(); constexpr std::string_view kExpectedKey{"chip"}; constexpr std::string_view kExpectedValue{"left-soc"}; EXPECT_EQ(key_value_pair.Next(), OkStatus()); EXPECT_EQ(key_value_pair.Field().value(), KeyValuePair::Fields::KEY); std::array key{}; StatusWithSize key_status = key_value_pair.ReadKey(key); EXPECT_EQ(key_status.status(), OkStatus()); EXPECT_EQ(key_status.size(), kExpectedKey.size()); EXPECT_EQ( std::memcmp(key.data(), kExpectedKey.data(), kExpectedKey.size()), 0); EXPECT_EQ(key_value_pair.Next(), OkStatus()); EXPECT_EQ(key_value_pair.Field().value(), KeyValuePair::Fields::VALUE); std::array value{}; StatusWithSize value_status = key_value_pair.ReadValue(value); EXPECT_EQ(value_status.status(), OkStatus()); EXPECT_EQ(value_status.size(), kExpectedValue.size()); EXPECT_EQ( std::memcmp( value.data(), kExpectedValue.data(), kExpectedValue.size()), 0); EXPECT_EQ(key_value_pair.Next(), Status::OutOfRange()); } EXPECT_EQ(device_info.Next(), OkStatus()); EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::STATUS); Result device_info_status = device_info.ReadStatus(); EXPECT_EQ(device_info_status.status(), OkStatus()); EXPECT_EQ(device_info_status.value(), DeviceInfo::DeviceStatus::PANIC); EXPECT_EQ(device_info.Next(), Status::OutOfRange()); } EXPECT_EQ(proto_pigweed.Next(), Status::OutOfRange()); } EXPECT_EQ(proto.Next(), Status::OutOfRange()); } for (int i = 0; i < 5; ++i) { EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ID); Proto::ID::StreamDecoder id = pigweed.GetIdDecoder(); EXPECT_EQ(id.Next(), OkStatus()); EXPECT_EQ(id.Field().value(), Proto::ID::Fields::ID); Result id_id = id.ReadId(); EXPECT_EQ(id_id.status(), OkStatus()); EXPECT_EQ(id_id.value(), 5u * i * i + 3 * i + 49); EXPECT_EQ(id.Next(), Status::OutOfRange()); } EXPECT_EQ(pigweed.Next(), Status::OutOfRange()); } TEST(Codegen, ResourceExhausted) { // clang-format off constexpr uint8_t proto_data[] = { // pigweed.error_message 0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ', 't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r', }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); Pigweed::StreamDecoder pigweed(reader); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE); std::array error_message{}; StatusWithSize error_message_status = pigweed.ReadErrorMessage(error_message); EXPECT_EQ(error_message_status.status(), Status::ResourceExhausted()); EXPECT_EQ(error_message_status.size(), 0u); EXPECT_EQ(pigweed.Next(), Status::OutOfRange()); } TEST(Codegen, BytesReader) { // clang-format off constexpr uint8_t proto_data[] = { // pigweed.error_message 0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ', 't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r', }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); Pigweed::StreamDecoder pigweed(reader); constexpr std::string_view kExpectedErrorMessage{"not a typewriter"}; EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE); { StreamDecoder::BytesReader bytes_reader = pigweed.GetErrorMessageReader(); EXPECT_EQ(bytes_reader.field_size(), kExpectedErrorMessage.size()); std::array error_message{}; Result result = bytes_reader.Read(error_message); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value().size(), kExpectedErrorMessage.size()); EXPECT_EQ(std::memcmp(result.value().data(), kExpectedErrorMessage.data(), kExpectedErrorMessage.size()), 0); result = bytes_reader.Read(error_message); EXPECT_EQ(result.status(), Status::OutOfRange()); } EXPECT_EQ(pigweed.Next(), Status::OutOfRange()); } TEST(Codegen, Enum) { // clang-format off constexpr uint8_t proto_data[] = { // pigweed.bin (value value) 0x40, 0x01, // pigweed.bin (invalid value) 0x40, 0xff, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); Pigweed::StreamDecoder pigweed(reader); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::BIN); Result bin = pigweed.ReadBin(); EXPECT_EQ(bin.status(), OkStatus()); EXPECT_EQ(bin.value(), Pigweed::Protobuf::Binary::ZERO); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::BIN); bin = pigweed.ReadBin(); EXPECT_EQ(bin.status(), Status::DataLoss()); } TEST(Codegen, ImportedEnum) { // clang-format off constexpr uint8_t proto_data[] = { // result.status (value value) 0x08, 0x01, // result.status (invalid value) 0x08, 0xff, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); TestResult::StreamDecoder test_result(reader); EXPECT_EQ(test_result.Next(), OkStatus()); EXPECT_EQ(test_result.Field().value(), TestResult::Fields::STATUS); Result status = test_result.ReadStatus(); EXPECT_EQ(status.status(), OkStatus()); EXPECT_EQ(status.value(), imported::Status::NOT_OK); EXPECT_EQ(test_result.Next(), OkStatus()); EXPECT_EQ(test_result.Field().value(), TestResult::Fields::STATUS); status = test_result.ReadStatus(); EXPECT_EQ(status.status(), Status::DataLoss()); } TEST(CodegenRepeated, NonPackedScalar) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x08, 0x00, 0x08, 0x10, 0x08, 0x20, 0x08, 0x30, // fixed32s[]. v={0, 16, 32, 48} 0x35, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, 0x35, 0x20, 0x00, 0x00, 0x00, 0x35, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); for (int i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); Result result = repeated_test.ReadUint32s(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } for (int i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); Result result = repeated_test.ReadFixed32s(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); } TEST(CodegenRepeated, NonPackedScalarVector) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x08, 0x00, 0x08, 0x10, 0x08, 0x20, 0x08, 0x30, // fixed32s[]. v={0, 16, 32, 48} 0x35, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, 0x35, 0x20, 0x00, 0x00, 0x00, 0x35, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); pw::Vector uint32s{}; for (int i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), i + 1u); } for (int i = 0; i < 4; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } pw::Vector fixed32s{}; for (int i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); Status status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), i + 1u); } for (int i = 0; i < 4; ++i) { EXPECT_EQ(fixed32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); } TEST(CodegenRepeated, NonPackedVarintScalarVectorFull) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x08, 0x00, 0x08, 0x10, 0x08, 0x20, 0x08, 0x30, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); pw::Vector uint32s{}; EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 1u); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 2u); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(uint32s.size(), 2u); for (int i = 0; i < 2; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } } TEST(CodegenRepeated, NonPackedFixedScalarVectorFull) { // clang-format off constexpr uint8_t proto_data[] = { // fixed32s[]. v={0, 16, 32, 48} 0x35, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, 0x35, 0x20, 0x00, 0x00, 0x00, 0x35, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); pw::Vector fixed32s{}; EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); Status status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 1u); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 2u); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(fixed32s.size(), 2u); for (int i = 0; i < 2; ++i) { EXPECT_EQ(fixed32s[i], i * 16u); } } TEST(CodegenRepeated, PackedScalar) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x0a, 0x04, 0x00, 0x10, 0x20, 0x30, // fixed32s[]. v={0, 16, 32, 48} 0x32, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); std::array uint32s{}; StatusWithSize sws = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(sws.status(), OkStatus()); EXPECT_EQ(sws.size(), 4u); for (int i = 0; i < 4; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); std::array fixed32s{}; sws = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(sws.status(), OkStatus()); EXPECT_EQ(sws.size(), 4u); for (int i = 0; i < 4; ++i) { EXPECT_EQ(fixed32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); } TEST(CodegenRepeated, PackedVarintScalarExhausted) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x0a, 0x04, 0x00, 0x10, 0x20, 0x30, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); std::array uint32s{}; StatusWithSize sws = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(sws.status(), Status::ResourceExhausted()); EXPECT_EQ(sws.size(), 2u); for (int i = 0; i < 2; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } } TEST(CodegenRepeated, PackedFixedScalarExhausted) { // clang-format off constexpr uint8_t proto_data[] = { // fixed32s[]. v={0, 16, 32, 48} 0x32, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); std::array fixed32s{}; StatusWithSize sws = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(sws.status(), Status::ResourceExhausted()); EXPECT_EQ(sws.size(), 0u); } TEST(CodegenRepeated, PackedScalarVector) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x0a, 0x04, 0x00, 0x10, 0x20, 0x30, // fixed32s[]. v={0, 16, 32, 48} 0x32, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); pw::Vector uint32s{}; Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 4u); for (int i = 0; i < 4; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); pw::Vector fixed32s{}; status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 4u); for (int i = 0; i < 4; ++i) { EXPECT_EQ(fixed32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); } TEST(CodegenRepeated, PackedVarintScalarVectorFull) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x0a, 0x04, 0x00, 0x10, 0x20, 0x30, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); pw::Vector uint32s{}; Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(uint32s.size(), 2u); for (int i = 0; i < 2; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } } TEST(CodegenRepeated, PackedFixedScalarVectorFull) { // clang-format off constexpr uint8_t proto_data[] = { // fixed32s[]. v={0, 16, 32, 48} 0x32, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); pw::Vector fixed32s{}; Status status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(fixed32s.size(), 0u); } TEST(CodegenRepeated, PackedScalarVectorRepeated) { // clang-format off constexpr uint8_t proto_data[] = { // uint32s[], v={0, 16, 32, 48} 0x0a, 0x04, 0x00, 0x10, 0x20, 0x30, // uint32s[], v={64, 80, 96, 112} 0x0a, 0x04, 0x40, 0x50, 0x60, 0x70, // fixed32s[]. v={0, 16, 32, 48} 0x32, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, // fixed32s[]. v={64, 80, 96, 112} 0x32, 0x10, 0x40, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); pw::Vector uint32s{}; Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 4u); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::UINT32S); status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 8u); for (int i = 0; i < 8; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); pw::Vector fixed32s{}; status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 4u); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::FIXED32S); status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 8u); for (int i = 0; i < 8; ++i) { EXPECT_EQ(fixed32s[i], i * 16u); } EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); } TEST(CodegenRepeated, NonScalar) { // clang-format off constexpr uint8_t proto_data[] = { // strings[], v={"the", "quick", "brown", "fox"} 0x1a, 0x03, 't', 'h', 'e', 0x1a, 0x5, 'q', 'u', 'i', 'c', 'k', 0x1a, 0x5, 'b', 'r', 'o', 'w', 'n', 0x1a, 0x3, 'f', 'o', 'x' }; // clang-format on stream::MemoryReader reader(std::as_bytes(std::span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); constexpr std::array kExpectedString{ {{"the"}, {"quick"}, {"brown"}, {"fox"}}}; for (int i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::STRINGS); std::array string{}; StatusWithSize sws = repeated_test.ReadStrings(string); EXPECT_EQ(sws.status(), OkStatus()); EXPECT_EQ(sws.size(), kExpectedString[i].size()); EXPECT_EQ(std::memcmp(string.data(), kExpectedString[i].data(), kExpectedString[i].size()), 0); } EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); } } // namespace } // namespace pw::protobuf