// 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 "pw_bytes/span.h" #include "pw_containers/vector.h" #include "pw_protobuf/find.h" #include "pw_span/span.h" #include "pw_status/status.h" #include "pw_status/status_with_size.h" #include "pw_stream/memory_stream.h" #include "pw_unit_test/framework.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 test::pwpb::Bool; using test::pwpb::Enum; namespace DeviceInfo = test::pwpb::DeviceInfo; namespace KeyValuePair = test::pwpb::KeyValuePair; namespace Pigweed = test::pwpb::Pigweed; namespace Proto = test::pwpb::Proto; namespace RepeatedTest = test::pwpb::RepeatedTest; namespace TestResult = test::pwpb::TestResult; namespace imported { using ::pw::protobuf::test::imported::pwpb::IsValidStatus; using ::pw::protobuf::test::imported::pwpb::Status; } // namespace imported 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::kMagicNumber); 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::kZiggy); 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::kErrorMessage); 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::kBin); 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::kPigweed); { Pigweed::Pigweed::StreamDecoder pigweed_pigweed = pigweed.GetPigweedDecoder(); EXPECT_EQ(pigweed_pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed_pigweed.Field().value(), Pigweed::Pigweed::Fields::kStatus); 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::kProto); { Proto::StreamDecoder proto = pigweed.GetProtoDecoder(); EXPECT_EQ(proto.Next(), OkStatus()); EXPECT_EQ(proto.Field().value(), Proto::Fields::kBin); 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::kPigweedPigweedBin); 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::kPigweedProtobufBin); 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::kMeta); { 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::kFileName); 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::kStatus); 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::kPigweed); { 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::kErrorMessage); 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::kMagicNumber); 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::kDeviceInfo); { DeviceInfo::StreamDecoder device_info = proto_pigweed.GetDeviceInfoDecoder(); EXPECT_EQ(device_info.Next(), OkStatus()); EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::kAttributes); { 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::kKey); 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::kValue); 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::kAttributes); { 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::kKey); 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::kValue); 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::kStatus); 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 (unsigned i = 0; i < 5; ++i) { EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::kId); Proto::ID::StreamDecoder id = pigweed.GetIdDecoder(); EXPECT_EQ(id.Next(), OkStatus()); EXPECT_EQ(id.Field().value(), Proto::ID::Fields::kId); 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(as_bytes(span(proto_data))); Pigweed::StreamDecoder pigweed(reader); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::kErrorMessage); 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(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::kErrorMessage); { 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 (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::kBin); Result 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::kBin); bin = pigweed.ReadBin(); EXPECT_EQ(bin.status(), OkStatus()); EXPECT_FALSE(Pigweed::Protobuf::IsValidBinary(bin.value())); EXPECT_EQ(static_cast(bin.value()), 0x7fu); EXPECT_EQ(pigweed.Next(), OkStatus()); EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::kBin); 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::kStatus); Result 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::kStatus); status = test_result.ReadStatus(); EXPECT_EQ(status.status(), OkStatus()); EXPECT_FALSE(imported::IsValidStatus(status.value())); EXPECT_EQ(static_cast(status.value()), 0x7fu); EXPECT_EQ(test_result.Next(), OkStatus()); EXPECT_EQ(test_result.Field().value(), TestResult::Fields::kStatus); 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 (uint32_t i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kUint32s); Result result = repeated_test.ReadUint32s(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } for (unsigned i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kFixed32s); 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(as_bytes(span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); pw::Vector uint32s{}; for (unsigned i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kUint32s); Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), i + 1u); } for (unsigned short i = 0; i < 4; ++i) { EXPECT_EQ(uint32s[i], i * 16u); } pw::Vector fixed32s{}; for (unsigned i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kFixed32s); Status status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), i + 1u); } for (unsigned short 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 uint32s{}; EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kUint32s); 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::kUint32s); 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::kUint32s); status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(uint32s.size(), 2u); for (unsigned short 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 fixed32s{}; EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kFixed32s); 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::kFixed32s); 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::kFixed32s); status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(fixed32s.size(), 2u); for (unsigned short 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::kUint32s); std::array uint32s{}; StatusWithSize sws = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(sws.status(), OkStatus()); EXPECT_EQ(sws.size(), 4u); for (unsigned short 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::kFixed32s); std::array fixed32s{}; sws = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(sws.status(), OkStatus()); EXPECT_EQ(sws.size(), 4u); for (unsigned short 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::kUint32s); std::array uint32s{}; StatusWithSize sws = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(sws.status(), Status::ResourceExhausted()); EXPECT_EQ(sws.size(), 2u); for (unsigned short 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::kFixed32s); 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(as_bytes(span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kUint32s); pw::Vector uint32s{}; Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 4u); for (unsigned short 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::kFixed32s); pw::Vector fixed32s{}; status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 4u); for (unsigned short 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::kUint32s); pw::Vector uint32s{}; Status status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, Status::ResourceExhausted()); EXPECT_EQ(uint32s.size(), 2u); for (unsigned short 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::kFixed32s); 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(as_bytes(span(proto_data))); RepeatedTest::StreamDecoder repeated_test(reader); EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kUint32s); 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::kUint32s); status = repeated_test.ReadUint32s(uint32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(uint32s.size(), 8u); for (unsigned short 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::kFixed32s); 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::kFixed32s); status = repeated_test.ReadFixed32s(fixed32s); EXPECT_EQ(status, OkStatus()); EXPECT_EQ(fixed32s.size(), 8u); for (unsigned short 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 kExpectedString{ {{"the"}, {"quick"}, {"brown"}, {"fox"}}}; for (unsigned short i = 0; i < 4; ++i) { EXPECT_EQ(repeated_test.Next(), OkStatus()); EXPECT_EQ(repeated_test.Field().value(), RepeatedTest::Fields::kStrings); 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()); } 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::kEnums); std::array enums{}; StatusWithSize sws = repeated_test.ReadEnums(enums); EXPECT_EQ(sws.status(), OkStatus()); ASSERT_EQ(sws.size(), 4u); for (unsigned short 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::kEnums); pw::Vector enums{}; Status status = repeated_test.ReadEnums(enums); EXPECT_EQ(status, OkStatus()); ASSERT_EQ(enums.size(), 4u); for (unsigned short 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(Codegen, FindBuffer) { // clang-format off constexpr uint8_t proto_data[] = { // pigweed.magic_number 0x08, 0x49, // pigweed.ziggy 0x10, 0xdd, 0x01, // pigweed.cycles 0x19, 0xde, 0xad, 0xca, 0xfe, 0x10, 0x20, 0x30, 0x40, // pigweed.ratio 0x25, 0x8f, 0xc2, 0xb5, 0xbf, // 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 EXPECT_EQ(Pigweed::FindMagicNumber(as_bytes(span(proto_data))).value(), 0x49u); EXPECT_EQ(Pigweed::FindZiggy(as_bytes(span(proto_data))).value(), -111); EXPECT_EQ(Pigweed::FindCycles(as_bytes(span(proto_data))).value(), 0x40302010fecaaddeu); EXPECT_EQ(Pigweed::FindRatio(as_bytes(span(proto_data))).value(), -1.42f); auto result = Pigweed::FindErrorMessage(as_bytes(span(proto_data))); EXPECT_EQ(result.status(), OkStatus()); InlineString<32> str(*result); EXPECT_STREQ(str.c_str(), "not a typewriter"); EXPECT_EQ(Pigweed::FindBin(as_bytes(span(proto_data))).value(), Pigweed::Protobuf::Binary::ZERO); Result pigweed = Pigweed::FindPigweed(as_bytes(span(proto_data))); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(pigweed->size(), 2u); EXPECT_EQ(Pigweed::Pigweed::FindStatus(*pigweed).value(), Bool::FILE_NOT_FOUND); // Nonexisting fields. EXPECT_EQ(Pigweed::FindData(as_bytes(span(proto_data))).status(), Status::NotFound()); EXPECT_EQ(Pigweed::FindDescription(as_bytes(span(proto_data))).status(), Status::NotFound()); EXPECT_EQ(Pigweed::FindSpecialProperty(as_bytes(span(proto_data))).status(), Status::NotFound()); EXPECT_EQ(Pigweed::FindBungle(as_bytes(span(proto_data))).status(), Status::NotFound()); } TEST(Codegen, FindStream) { stream::MemoryReader reader({}); // clang-format off constexpr uint8_t proto_data[] = { // pigweed.magic_number 0x08, 0x49, // pigweed.ziggy 0x10, 0xdd, 0x01, // pigweed.cycles 0x19, 0xde, 0xad, 0xca, 0xfe, 0x10, 0x20, 0x30, 0x40, // pigweed.ratio 0x25, 0x8f, 0xc2, 0xb5, 0xbf, // 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 reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindMagicNumber(reader).value(), 0x49u); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindZiggy(reader).value(), -111); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindCycles(reader).value(), 0x40302010fecaaddeu); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindRatio(reader).value(), -1.42f); reader = stream::MemoryReader(as_bytes(span(proto_data))); char str[32] = {'\0'}; auto result = Pigweed::FindErrorMessage(reader, str); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.size(), 16u); EXPECT_STREQ(str, "not a typewriter"); reader = stream::MemoryReader(as_bytes(span(proto_data))); InlineString<32> error_message; result = Pigweed::FindErrorMessage(reader, error_message); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.size(), 16u); EXPECT_STREQ(error_message.c_str(), "not a typewriter"); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindBin(reader).value(), Pigweed::Protobuf::Binary::ZERO); // Nonexisting fields. reader = stream::MemoryReader(as_bytes(span(proto_data))); std::byte buf[32]; EXPECT_EQ(Pigweed::FindData(reader, buf).status(), Status::NotFound()); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindDescription(reader, str).status(), Status::NotFound()); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindSpecialProperty(reader).status(), Status::NotFound()); reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindBungle(reader).status(), Status::NotFound()); // Advance the stream past `magic_number`, then attempt to find it. reader = stream::MemoryReader(as_bytes(span(proto_data))); EXPECT_EQ(Pigweed::FindZiggy(reader).status(), OkStatus()); EXPECT_EQ(Pigweed::FindMagicNumber(reader).status(), Status::NotFound()); } TEST(CodegenRepeated, Find) { // 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 Uint32Finder uint32s_finder = RepeatedTest::FindUint32s(as_bytes(span(proto_data))); for (uint32_t i = 0; i < 4; ++i) { Result result = uint32s_finder.Next(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } EXPECT_EQ(uint32s_finder.Next().status(), Status::NotFound()); Fixed32Finder fixed32s_finder = RepeatedTest::FindFixed32s(as_bytes(span(proto_data))); for (unsigned i = 0; i < 4; ++i) { Result result = fixed32s_finder.Next(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } EXPECT_EQ(fixed32s_finder.Next().status(), Status::NotFound()); } TEST(CodegenRepeated, FindStream) { // 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))); Uint32StreamFinder uint32s_finder = RepeatedTest::FindUint32s(reader); for (uint32_t i = 0; i < 4; ++i) { Result result = uint32s_finder.Next(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } EXPECT_EQ(uint32s_finder.Next().status(), Status::NotFound()); ASSERT_EQ(reader.Seek(0), OkStatus()); Fixed32StreamFinder fixed32s_finder = RepeatedTest::FindFixed32s(reader); for (unsigned i = 0; i < 4; ++i) { Result result = fixed32s_finder.Next(); EXPECT_EQ(result.status(), OkStatus()); EXPECT_EQ(result.value(), i * 16u); } EXPECT_EQ(fixed32s_finder.Next().status(), Status::NotFound()); } } // namespace } // namespace pw::protobuf