| // 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 <array> |
| #include <stdexcept> |
| #include <string_view> |
| |
| #include "gtest/gtest.h" |
| #include "pw_bytes/span.h" |
| #include "pw_containers/vector.h" |
| #include "pw_span/span.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(as_bytes(span(proto_data))); |
| Pigweed::StreamDecoder pigweed(reader); |
| |
| EXPECT_EQ(pigweed.Next(), OkStatus()); |
| EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::MAGIC_NUMBER); |
| Result<uint32_t> 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<int32_t> 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<char, 32> 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<Pigweed::Protobuf::Binary> 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<Bool> 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::Binary> 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<Pigweed::Pigweed::Binary> 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<Pigweed::Protobuf::Binary> 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<char, 32> 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<Pigweed::Protobuf::Compiler::Status> 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<char, 32> 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<uint32_t> 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<char, 32> 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<char, 32> 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<char, 32> 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<char, 32> 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<DeviceInfo::DeviceStatus> 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<uint32_t> 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(as_bytes(span(proto_data))); |
| Pigweed::StreamDecoder pigweed(reader); |
| |
| EXPECT_EQ(pigweed.Next(), OkStatus()); |
| EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE); |
| std::array<char, 8> 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(as_bytes(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<std::byte, 32> error_message{}; |
| Result<ByteSpan> 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 (valid value) |
| 0x40, 0x01, |
| // pigweed.bin (unknown value) |
| 0x40, 0x7f, |
| // pigweed.bin (invalid value) |
| 0x40, 0xff, |
| }; |
| // clang-format on |
| |
| stream::MemoryReader reader(as_bytes(span(proto_data))); |
| Pigweed::StreamDecoder pigweed(reader); |
| |
| EXPECT_EQ(pigweed.Next(), OkStatus()); |
| EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::BIN); |
| Result<Pigweed::Protobuf::Binary> bin = pigweed.ReadBin(); |
| EXPECT_EQ(bin.status(), OkStatus()); |
| EXPECT_TRUE(Pigweed::Protobuf::IsValidBinary(bin.value())); |
| 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(), OkStatus()); |
| EXPECT_FALSE(Pigweed::Protobuf::IsValidBinary(bin.value())); |
| EXPECT_EQ(static_cast<uint32_t>(bin.value()), 0x7fu); |
| |
| 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 (valid value) |
| 0x08, 0x01, |
| // result.status (unknown value) |
| 0x08, 0x7f, |
| // result.status (invalid value) |
| 0x08, 0xff, |
| }; |
| // clang-format on |
| |
| stream::MemoryReader reader(as_bytes(span(proto_data))); |
| TestResult::StreamDecoder test_result(reader); |
| |
| EXPECT_EQ(test_result.Next(), OkStatus()); |
| EXPECT_EQ(test_result.Field().value(), TestResult::Fields::STATUS); |
| Result<imported::Status> status = test_result.ReadStatus(); |
| EXPECT_EQ(status.status(), OkStatus()); |
| EXPECT_TRUE(imported::IsValidStatus(status.value())); |
| 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(), OkStatus()); |
| EXPECT_FALSE(imported::IsValidStatus(status.value())); |
| EXPECT_EQ(static_cast<uint32_t>(status.value()), 0x7fu); |
| |
| 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(as_bytes(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<uint32_t> 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<uint32_t> 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(as_bytes(span(proto_data))); |
| RepeatedTest::StreamDecoder repeated_test(reader); |
| |
| pw::Vector<uint32_t, 8> 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<uint32_t, 8> 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(as_bytes(span(proto_data))); |
| RepeatedTest::StreamDecoder repeated_test(reader); |
| |
| pw::Vector<uint32_t, 2> 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(as_bytes(span(proto_data))); |
| RepeatedTest::StreamDecoder repeated_test(reader); |
| |
| pw::Vector<uint32_t, 2> 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(as_bytes(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<uint32_t, 8> 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<uint32_t, 8> 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(as_bytes(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<uint32_t, 2> 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(as_bytes(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<uint32_t, 2> 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(as_bytes(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<uint32_t, 8> 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<uint32_t, 8> 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(as_bytes(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<uint32_t, 2> 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(as_bytes(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<uint32_t, 2> 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(as_bytes(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<uint32_t, 8> 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<uint32_t, 8> 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(as_bytes(span(proto_data))); |
| RepeatedTest::StreamDecoder repeated_test(reader); |
| |
| constexpr std::array<std::string_view, 4> 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<char, 32> 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()); |
| } |
| |
| TEST(CodegenRepeated, PackedEnum) { |
| // clang-format off |
| constexpr uint8_t proto_data[] = { |
| // enums[], v={RED, GREEN, AMBER, RED} |
| 0x4a, 0x04, 0x00, 0x02, 0x01, 0x00, |
| }; |
| // clang-format on |
| |
| stream::MemoryReader reader(as_bytes(span(proto_data))); |
| RepeatedTest::StreamDecoder repeated_test(reader); |
| |
| EXPECT_EQ(repeated_test.Next(), OkStatus()); |
| EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::ENUMS); |
| std::array<Enum, 4> enums{}; |
| StatusWithSize sws = repeated_test.ReadEnums(enums); |
| EXPECT_EQ(sws.status(), OkStatus()); |
| ASSERT_EQ(sws.size(), 4u); |
| |
| for (int i = 0; i < 4; ++i) { |
| EXPECT_TRUE(IsValidEnum(enums[i])); |
| } |
| |
| EXPECT_EQ(enums[0], Enum::RED); |
| EXPECT_EQ(enums[1], Enum::GREEN); |
| EXPECT_EQ(enums[2], Enum::AMBER); |
| EXPECT_EQ(enums[3], Enum::RED); |
| |
| EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); |
| } |
| |
| TEST(CodegenRepeated, PackedEnumVector) { |
| // clang-format off |
| constexpr uint8_t proto_data[] = { |
| // enums[], v={RED, GREEN, AMBER, RED} |
| 0x4a, 0x04, 0x00, 0x02, 0x01, 0x00, |
| }; |
| // clang-format on |
| |
| stream::MemoryReader reader(as_bytes(span(proto_data))); |
| RepeatedTest::StreamDecoder repeated_test(reader); |
| |
| EXPECT_EQ(repeated_test.Next(), OkStatus()); |
| EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::ENUMS); |
| pw::Vector<Enum, 4> enums{}; |
| Status status = repeated_test.ReadEnums(enums); |
| EXPECT_EQ(status, OkStatus()); |
| ASSERT_EQ(enums.size(), 4u); |
| |
| for (int i = 0; i < 4; ++i) { |
| EXPECT_TRUE(IsValidEnum(enums[i])); |
| } |
| |
| EXPECT_EQ(enums[0], Enum::RED); |
| EXPECT_EQ(enums[1], Enum::GREEN); |
| EXPECT_EQ(enums[2], Enum::AMBER); |
| EXPECT_EQ(enums[3], Enum::RED); |
| |
| EXPECT_EQ(repeated_test.Next(), Status::OutOfRange()); |
| } |
| |
| } // namespace |
| } // namespace pw::protobuf |