blob: 4082070d91f6e2534d65c8918ac0ec0f0984249f [file] [log] [blame]
// 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