/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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
 *
 *      http://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 "perfetto/protozero/proto_decoder.h"

#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/message.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/protozero/static_buffer.h"
#include "test/gtest_and_gmock.h"

#include "src/protozero/test/example_proto/test_messages.pb.h"
#include "src/protozero/test/example_proto/test_messages.pbzero.h"

// Generated by the protozero plugin.
namespace pbtest = protozero::test::protos::pbzero;

// Generated by the official protobuf compiler.
namespace pbgold = protozero::test::protos;

namespace protozero {
namespace {

using ::testing::_;
using ::testing::ElementsAre;
using ::testing::InSequence;
using ::testing::Invoke;
using namespace proto_utils;

TEST(ProtoDecoderTest, ReadString) {
  HeapBuffered<Message> message;

  static constexpr char kTestString[] = "test";
  message->AppendString(1, kTestString);
  std::vector<uint8_t> proto = message.SerializeAsArray();
  TypedProtoDecoder<32, false> decoder(proto.data(), proto.size());

  const auto& field = decoder.Get(1);
  ASSERT_EQ(field.type(), ProtoWireType::kLengthDelimited);
  ASSERT_EQ(field.size(), sizeof(kTestString) - 1);
  for (size_t i = 0; i < sizeof(kTestString) - 1; i++) {
    ASSERT_EQ(field.data()[i], kTestString[i]);
  }
}

TEST(ProtoDecoderTest, SkipVeryLargeFields) {
  const size_t kPayloadSize = 257 * 1024 * 1024;
  const uint64_t data_size = 4096 + kPayloadSize;
  std::unique_ptr<uint8_t, perfetto::base::FreeDeleter> data(
      static_cast<uint8_t*>(malloc(data_size)));
  StaticBuffered<Message> message(data.get(), data_size);

  // Append a valid field.
  message->AppendVarInt(/*field_id=*/1, 11);

  // Append a very large field that will be skipped.
  uint8_t raw[10];
  uint8_t* wptr = raw;
  wptr = WriteVarInt(MakeTagLengthDelimited(2), wptr);
  wptr = WriteVarInt(kPayloadSize, wptr);
  message->AppendRawProtoBytes(raw, static_cast<size_t>(wptr - raw));
  const size_t kPaddingSize = 1024 * 128;
  std::unique_ptr<uint8_t[]> padding(new uint8_t[kPaddingSize]());
  for (size_t i = 0; i < kPayloadSize / kPaddingSize; i++)
    message->AppendRawProtoBytes(padding.get(), kPaddingSize);

  // Append another valid field.
  message->AppendVarInt(/*field_id=*/3, 13);

  ProtoDecoder decoder(data.get(), message.Finalize());
  Field field = decoder.ReadField();
  ASSERT_EQ(1u, field.id());
  ASSERT_EQ(11, field.as_int32());

  field = decoder.ReadField();
  ASSERT_EQ(3u, field.id());
  ASSERT_EQ(13, field.as_int32());

  field = decoder.ReadField();
  ASSERT_FALSE(field.valid());
}

TEST(ProtoDecoderTest, SingleRepeatedField) {
  HeapBuffered<Message> message;
  message->AppendVarInt(/*field_id=*/2, 10);
  auto data = message.SerializeAsArray();
  TypedProtoDecoder<2, true> tpd(data.data(), data.size());
  auto it = tpd.GetRepeated<int32_t>(/*field_id=*/2);
  EXPECT_TRUE(it);
  EXPECT_EQ(it.field().as_int32(), 10);
  EXPECT_EQ(*it, 10);
  EXPECT_FALSE(++it);
}

TEST(ProtoDecoderTest, RepeatedVariableLengthField) {
  HeapBuffered<Message> message;

  static constexpr char kTestString[] = "test";
  static constexpr char kTestString2[] = "honk honk";
  message->AppendString(1, kTestString);
  message->AppendString(1, kTestString2);
  std::vector<uint8_t> proto = message.SerializeAsArray();
  TypedProtoDecoder<32, false> decoder(proto.data(), proto.size());

  auto it = decoder.GetRepeated<ConstChars>(1);
  ASSERT_EQ(it->type(), ProtoWireType::kLengthDelimited);
  ASSERT_EQ(it->size(), sizeof(kTestString) - 1);
  ASSERT_EQ(it->as_std_string(), std::string(kTestString));
  ASSERT_EQ((*it).ToStdString(), std::string(kTestString));
  ++it;
  ASSERT_EQ(it->type(), ProtoWireType::kLengthDelimited);
  ASSERT_EQ(it->size(), sizeof(kTestString2) - 1);
  ASSERT_EQ(it->as_std_string(), std::string(kTestString2));
  ASSERT_EQ((*it).ToStdString(), std::string(kTestString2));
}

TEST(ProtoDecoderTest, SingleRepeatedFieldWithExpansion) {
  HeapBuffered<Message> message;
  for (int i = 0; i < 2000; i++) {
    message->AppendVarInt(/*field_id=*/2, i);
  }
  auto data = message.SerializeAsArray();
  TypedProtoDecoder<2, true> tpd(data.data(), data.size());
  auto it = tpd.GetRepeated<int32_t>(/*field_id=*/2);
  for (int i = 0; i < 2000; i++) {
    EXPECT_TRUE(it);
    EXPECT_EQ(*it, i);
    ++it;
  }
  EXPECT_FALSE(it);
}

TEST(ProtoDecoderTest, NoRepeatedField) {
  uint8_t buf[] = {0x01};
  TypedProtoDecoder<2, true> tpd(buf, 1);
  auto it = tpd.GetRepeated<int32_t>(/*field_id=*/1);
  EXPECT_FALSE(it);
  EXPECT_FALSE(tpd.Get(2).valid());
}

TEST(ProtoDecoderTest, RepeatedFields) {
  HeapBuffered<Message> message;

  message->AppendVarInt(1, 10);
  message->AppendVarInt(2, 20);
  message->AppendVarInt(3, 30);

  message->AppendVarInt(1, 11);
  message->AppendVarInt(2, 21);
  message->AppendVarInt(2, 22);

  // When iterating with the simple decoder we should just see fields in parsing
  // order.
  auto data = message.SerializeAsArray();
  ProtoDecoder decoder(data.data(), data.size());
  std::string fields_seen;
  for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) {
    fields_seen +=
        std::to_string(fld.id()) + ":" + std::to_string(fld.as_int32()) + ";";
  }
  EXPECT_EQ(fields_seen, "1:10;2:20;3:30;1:11;2:21;2:22;");

  TypedProtoDecoder<4, true> tpd(data.data(), data.size());

  // When parsing with the one-shot decoder and querying the single field id, we
  // should see the last value for each of them, not the first one. This is the
  // current behavior of Google protobuf's parser.
  EXPECT_EQ(tpd.Get(1).as_int32(), 11);
  EXPECT_EQ(tpd.Get(2).as_int32(), 22);
  EXPECT_EQ(tpd.Get(3).as_int32(), 30);

  // But when iterating we should see values in the original order.
  auto it = tpd.GetRepeated<int32_t>(1);
  EXPECT_EQ(*it, 10);
  EXPECT_EQ(*++it, 11);
  EXPECT_FALSE(++it);

  it = tpd.GetRepeated<int32_t>(2);
  EXPECT_EQ(*it++, 20);
  EXPECT_EQ(*it++, 21);
  EXPECT_EQ(*it++, 22);
  EXPECT_FALSE(it);

  it = tpd.GetRepeated<int32_t>(3);
  EXPECT_EQ(*it, 30);
  EXPECT_FALSE(++it);
}

TEST(ProtoDecoderTest, FixedData) {
  struct FieldExpectation {
    const char* encoded;
    size_t encoded_size;
    uint32_t id;
    ProtoWireType type;
    uint64_t int_value;
  };

  const FieldExpectation kFieldExpectations[] = {
      {"\x08\x00", 2, 1, ProtoWireType::kVarInt, 0},
      {"\x08\x01", 2, 1, ProtoWireType::kVarInt, 1},
      {"\x08\x42", 2, 1, ProtoWireType::kVarInt, 0x42},
      {"\xF8\x07\x42", 3, 127, ProtoWireType::kVarInt, 0x42},
      {"\xB8\x3E\xFF\xFF\xFF\xFF\x0F", 7, 999, ProtoWireType::kVarInt,
       0xFFFFFFFF},
      {"\x7D\x42\x00\x00\x00", 5, 15, ProtoWireType::kFixed32, 0x42},
      {"\xBD\x3E\x78\x56\x34\x12", 6, 999, ProtoWireType::kFixed32, 0x12345678},
      {"\x79\x42\x00\x00\x00\x00\x00\x00\x00", 9, 15, ProtoWireType::kFixed64,
       0x42},
      {"\xB9\x3E\x08\x07\x06\x05\x04\x03\x02\x01", 10, 999,
       ProtoWireType::kFixed64, 0x0102030405060708},
      {"\x0A\x00", 2, 1, ProtoWireType::kLengthDelimited, 0},
      {"\x0A\x04|abc", 6, 1, ProtoWireType::kLengthDelimited, 4},
      {"\xBA\x3E\x04|abc", 7, 999, ProtoWireType::kLengthDelimited, 4},
      {"\xBA\x3E\x83\x01|abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab"
       "cdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu"
       "vwx",
       135, 999, ProtoWireType::kLengthDelimited, 131},
  };

  for (size_t i = 0; i < perfetto::base::ArraySize(kFieldExpectations); ++i) {
    const FieldExpectation& exp = kFieldExpectations[i];
    TypedProtoDecoder<999, 0> decoder(
        reinterpret_cast<const uint8_t*>(exp.encoded), exp.encoded_size);

    auto& field = decoder.Get(exp.id);
    ASSERT_EQ(exp.type, field.type());

    if (field.type() == ProtoWireType::kLengthDelimited) {
      ASSERT_EQ(exp.int_value, field.size());
    } else {
      ASSERT_EQ(int64_t(exp.int_value), field.as_int64());
      // Proto encodes booleans as varints of 0 or 1.
      if (exp.int_value == 0 || exp.int_value == 1) {
        ASSERT_EQ(int64_t(exp.int_value), field.as_bool());
      }
    }
  }

  // Test float and doubles decoding.
  const char buf[] = "\x0d\x00\x00\xa0\x3f\x11\x00\x00\x00\x00\x00\x42\x8f\xc0";
  TypedProtoDecoder<2, false> decoder(reinterpret_cast<const uint8_t*>(buf),
                                      sizeof(buf));
  EXPECT_FLOAT_EQ(decoder.Get(1).as_float(), 1.25f);
  EXPECT_DOUBLE_EQ(decoder.Get(2).as_double(), -1000.25);
}

TEST(ProtoDecoderTest, FindField) {
  uint8_t buf[] = {0x08, 0x00};  // field_id 1, varint value 0.
  ProtoDecoder pd(buf, 2);

  auto field = pd.FindField(1);
  ASSERT_TRUE(field);
  EXPECT_EQ(field.as_int64(), 0);

  auto field2 = pd.FindField(2);
  EXPECT_FALSE(field2);
}

TEST(ProtoDecoderTest, MoveTypedDecoder) {
  HeapBuffered<Message> message;
  message->AppendVarInt(/*field_id=*/1, 10);
  std::vector<uint8_t> proto = message.SerializeAsArray();

  // Construct a decoder that uses inline storage (i.e., the fields are stored
  // within the object itself).
  using Decoder = TypedProtoDecoder<32, false>;
  std::unique_ptr<Decoder> decoder(new Decoder(proto.data(), proto.size()));
  ASSERT_GE(reinterpret_cast<uintptr_t>(&decoder->at<1>()),
            reinterpret_cast<uintptr_t>(decoder.get()));
  ASSERT_LT(reinterpret_cast<uintptr_t>(&decoder->at<1>()),
            reinterpret_cast<uintptr_t>(decoder.get()) + sizeof(Decoder));

  // Move the decoder into another object and deallocate the original object.
  Decoder decoder2(std::move(*decoder));
  decoder.reset();

  // Check that the contents got moved correctly.
  EXPECT_EQ(decoder2.Get(1).as_int32(), 10);
  ASSERT_GE(reinterpret_cast<uintptr_t>(&decoder2.at<1>()),
            reinterpret_cast<uintptr_t>(&decoder2));
  ASSERT_LT(reinterpret_cast<uintptr_t>(&decoder2.at<1>()),
            reinterpret_cast<uintptr_t>(&decoder2) + sizeof(Decoder));
}

TEST(ProtoDecoderTest, PackedRepeatedVarint) {
  std::vector<int32_t> values = {42, 255, 0, -1};

  // serialize using protobuf library
  pbgold::PackedRepeatedFields msg;
  for (auto v : values)
    msg.add_field_int32(v);
  std::string serialized = msg.SerializeAsString();

  // decode using TypedProtoDecoder directly
  {
    constexpr int kFieldId =
        pbtest::PackedRepeatedFields::kFieldInt32FieldNumber;
    TypedProtoDecoder<kFieldId, false> decoder(
        reinterpret_cast<const uint8_t*>(serialized.data()), serialized.size());
    ASSERT_TRUE(decoder.at<kFieldId>().valid());
    bool parse_error = false;
    auto packed_it =
        decoder.GetPackedRepeated<proto_utils::ProtoWireType::kVarInt, int32_t>(
            kFieldId, &parse_error);

    std::vector<int32_t> decoded_values;
    for (; packed_it; ++packed_it) {
      auto v = *packed_it;
      decoded_values.push_back(v);
    }
    ASSERT_EQ(values, decoded_values);
    ASSERT_FALSE(parse_error);
  }

  // decode using plugin-generated accessor
  {
    auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized);
    ASSERT_TRUE(decoder.has_field_int32());

    bool parse_error = false;
    std::vector<int32_t> decoded_values;
    for (auto packed_it = decoder.field_int32(&parse_error); packed_it;
         ++packed_it) {
      auto v = *packed_it;
      decoded_values.push_back(v);
    }
    ASSERT_EQ(values, decoded_values);
    ASSERT_FALSE(parse_error);
  }

  // unset field case
  pbgold::PackedRepeatedFields empty_msg;
  std::string empty_serialized = empty_msg.SerializeAsString();
  auto decoder = pbtest::PackedRepeatedFields::Decoder(empty_serialized);
  ASSERT_FALSE(decoder.has_field_int32());
  bool parse_error = false;
  auto packed_it = decoder.field_int32(&parse_error);
  ASSERT_FALSE(bool(packed_it));
  ASSERT_FALSE(parse_error);
}

TEST(ProtoDecoderTest, PackedRepeatedFixed32) {
  std::vector<uint32_t> values = {42, 255, 0, 1};

  // serialize using protobuf library
  pbgold::PackedRepeatedFields msg;
  for (auto v : values)
    msg.add_field_fixed32(v);
  std::string serialized = msg.SerializeAsString();

  // decode using TypedProtoDecoder directly
  {
    constexpr int kFieldId =
        pbtest::PackedRepeatedFields::kFieldFixed32FieldNumber;
    TypedProtoDecoder<kFieldId, false> decoder(
        reinterpret_cast<const uint8_t*>(serialized.data()), serialized.size());
    bool parse_error = false;
    auto packed_it =
        decoder
            .GetPackedRepeated<proto_utils::ProtoWireType::kFixed32, uint32_t>(
                kFieldId, &parse_error);

    std::vector<uint32_t> decoded_values;
    for (; packed_it; ++packed_it) {
      auto v = *packed_it;
      decoded_values.push_back(v);
    }
    ASSERT_EQ(values, decoded_values);
    ASSERT_FALSE(parse_error);
  }

  // decode using plugin-generated accessor
  {
    auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized);
    ASSERT_TRUE(decoder.has_field_fixed32());

    bool parse_error = false;
    std::vector<uint32_t> decoded_values;
    for (auto packed_it = decoder.field_fixed32(&parse_error); packed_it;
         packed_it++) {
      auto v = *packed_it;
      decoded_values.push_back(v);
    }
    ASSERT_EQ(values, decoded_values);
    ASSERT_FALSE(parse_error);
  }

  // unset field case
  pbgold::PackedRepeatedFields empty_msg;
  std::string empty_serialized = empty_msg.SerializeAsString();
  auto decoder = pbtest::PackedRepeatedFields::Decoder(empty_serialized);
  ASSERT_FALSE(decoder.has_field_fixed32());
  bool parse_error = false;
  auto packed_it = decoder.field_fixed32(&parse_error);
  ASSERT_FALSE(bool(packed_it));
  ASSERT_FALSE(parse_error);
}

TEST(ProtoDecoderTest, PackedRepeatedFixed64) {
  std::vector<int64_t> values = {42, 255, 0, -1};

  // serialize using protobuf library
  pbgold::PackedRepeatedFields msg;
  for (auto v : values)
    msg.add_field_sfixed64(v);
  std::string serialized = msg.SerializeAsString();

  // decode using TypedProtoDecoder directly
  {
    constexpr int kFieldId =
        pbtest::PackedRepeatedFields::kFieldSfixed64FieldNumber;
    TypedProtoDecoder<kFieldId, false> decoder(
        reinterpret_cast<const uint8_t*>(serialized.data()), serialized.size());
    bool parse_error = false;
    auto packed_it =
        decoder
            .GetPackedRepeated<proto_utils::ProtoWireType::kFixed64, int64_t>(
                kFieldId, &parse_error);

    std::vector<int64_t> decoded_values;
    for (; packed_it; ++packed_it) {
      auto v = *packed_it;
      decoded_values.push_back(v);
    }
    ASSERT_EQ(values, decoded_values);
    ASSERT_FALSE(parse_error);
  }

  // decode using plugin-generated accessor
  {
    auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized);
    ASSERT_TRUE(decoder.has_field_sfixed64());

    bool parse_error = false;
    std::vector<int64_t> decoded_values;
    for (auto packed_it = decoder.field_sfixed64(&parse_error); packed_it;
         packed_it++) {
      auto v = *packed_it;
      decoded_values.push_back(v);
    }
    ASSERT_EQ(values, decoded_values);
    ASSERT_FALSE(parse_error);
  }

  // unset field case
  pbgold::PackedRepeatedFields empty_msg;
  std::string empty_serialized = empty_msg.SerializeAsString();
  auto decoder = pbtest::PackedRepeatedFields::Decoder(empty_serialized);
  ASSERT_FALSE(decoder.has_field_sfixed64());
  bool parse_error = false;
  auto packed_it = decoder.field_sfixed64(&parse_error);
  ASSERT_FALSE(bool(packed_it));
  ASSERT_FALSE(parse_error);
}

TEST(ProtoDecoderTest, ZeroLengthPackedRepeatedField) {
  HeapBuffered<pbtest::PackedRepeatedFields> msg;
  PackedVarInt buf;
  msg->set_field_int32(buf);
  std::string serialized = msg.SerializeAsString();

  // Encoded as 2 bytes: tag/field, and a length of zero.
  EXPECT_EQ(2u, serialized.size());

  // Appears empty when decoded.
  auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized);
  ASSERT_TRUE(decoder.has_field_int32());
  bool parse_error = false;
  auto packed_it = decoder.field_int32(&parse_error);
  ASSERT_FALSE(bool(packed_it));
  ASSERT_FALSE(parse_error);
}

TEST(ProtoDecoderTest, MalformedPackedFixedBuffer) {
  // Encode a fixed32 field where the length is not a multiple of 4 bytes.
  HeapBuffered<pbtest::PackedRepeatedFields> msg;
  PackedFixedSizeInt<uint32_t> buf;
  buf.Append(1);
  buf.Append(2);
  buf.Append(3);
  const uint8_t* data = buf.data();
  size_t size = buf.size();
  size_t invalid_size = size - 2;
  constexpr int kFieldId =
      pbtest::PackedRepeatedFields::kFieldFixed32FieldNumber;
  msg->AppendBytes(kFieldId, data, invalid_size);
  std::string serialized = msg.SerializeAsString();

  // Iterator indicates parse error.
  auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized);
  ASSERT_TRUE(decoder.has_field_fixed32());
  bool parse_error = false;
  for (auto packed_it = decoder.field_fixed32(&parse_error); packed_it;
       packed_it++) {
  }
  ASSERT_TRUE(parse_error);
}

TEST(ProtoDecoderTest, MalformedPackedVarIntBuffer) {
  // Encode a varint field with the last varint chopped off partway.
  HeapBuffered<pbtest::PackedRepeatedFields> msg;
  PackedVarInt buf;
  buf.Append(1024);
  buf.Append(2048);
  buf.Append(4096);
  const uint8_t* data = buf.data();
  size_t size = buf.size();
  size_t invalid_size = size - 1;
  constexpr int kFieldId = pbtest::PackedRepeatedFields::kFieldInt32FieldNumber;
  msg->AppendBytes(kFieldId, data, invalid_size);
  std::string serialized = msg.SerializeAsString();

  // Iterator indicates parse error.
  auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized);
  ASSERT_TRUE(decoder.has_field_int32());
  bool parse_error = false;
  for (auto packed_it = decoder.field_int32(&parse_error); packed_it;
       packed_it++) {
  }
  ASSERT_TRUE(parse_error);
}

// Tests that:
// 1. Very big field ids (>= 2**24) are just skipped but don't fail parsing.
//    This is a regression test for b/145339282 (DataSourceConfig.for_testing
//    having a very large ID == 268435455 until Android R).
// 2. Moderately big" field ids can be parsed correctly. See also
//    https://github.com/google/perfetto/issues/510 .
TEST(ProtoDecoderTest, BigFieldIds) {
  HeapBuffered<Message> message;
  message->AppendVarInt(/*field_id=*/1, 11);
  message->AppendVarInt(/*field_id=*/1 << 24, 0);  // Will be skipped
  message->AppendVarInt(/*field_id=*/65535, 99);
  message->AppendVarInt(/*field_id=*/(1 << 24) + 1023,
                        0);  // Will be skipped
  message->AppendVarInt(/*field_id=*/2, 12);
  message->AppendVarInt(/*field_id=*/1 << 28, 0);  // Will be skipped

  message->AppendVarInt(/*field_id=*/(1 << 24) - 1, 13);
  auto data = message.SerializeAsArray();

  // Check the iterator-based ProtoDecoder.
  {
    ProtoDecoder decoder(data.data(), data.size());
    Field field = decoder.ReadField();
    ASSERT_TRUE(field.valid());
    ASSERT_EQ(field.id(), 1u);
    ASSERT_EQ(field.as_int32(), 11);

    field = decoder.ReadField();
    ASSERT_TRUE(field.valid());
    ASSERT_EQ(field.id(), 65535u);
    ASSERT_EQ(field.as_int32(), 99);

    field = decoder.ReadField();
    ASSERT_TRUE(field.valid());
    ASSERT_EQ(field.id(), 2u);
    ASSERT_EQ(field.as_int32(), 12);

    field = decoder.ReadField();
    ASSERT_TRUE(field.valid());
    ASSERT_EQ(field.id(), (1u << 24) - 1u);
    ASSERT_EQ(field.as_int32(), 13);

    field = decoder.ReadField();
    ASSERT_FALSE(field.valid());
  }

  // Test the one-shot-read TypedProtoDecoder.
  // Note: field 65535 will be also skipped because this TypedProtoDecoder has
  // a cap on MAX_FIELD_ID = 3.
  {
    TypedProtoDecoder<3, true> tpd(data.data(), data.size());
    EXPECT_EQ(tpd.Get(1).as_int32(), 11);
    EXPECT_EQ(tpd.Get(2).as_int32(), 12);
  }
}

// Edge case for SkipBigFieldIds, the message contains only one field with a
// very big id. Test that we skip it and return an invalid field, instead of
// geetting stuck in some loop.
TEST(ProtoDecoderTest, OneBigFieldIdOnly) {
  HeapBuffered<Message> message;
  message->AppendVarInt(/*field_id=*/268435455, 0);
  auto data = message.SerializeAsArray();

  // Check the iterator-based ProtoDecoder.
  ProtoDecoder decoder(data.data(), data.size());
  Field field = decoder.ReadField();
  ASSERT_FALSE(field.valid());
}

// Check what happens when trying to parse packed repeated field and finding a
// mismatching wire type instead. A compliant protobuf decoder should accept it,
// but protozero doesn't handle that. At least it shouldn't crash.
TEST(ProtoDecoderTest, PacketRepeatedWireTypeMismatch) {
  protozero::HeapBuffered<pbtest::PackedRepeatedFields> message;
  // A proper packed encoding should have a length delimited wire type. Use a
  // var int wire type instead.
  constexpr int kFieldId = pbtest::PackedRepeatedFields::kFieldInt32FieldNumber;
  message->AppendTinyVarInt(kFieldId, 5);
  auto data = message.SerializeAsArray();

  pbtest::PackedRepeatedFields::Decoder decoder(data.data(), data.size());
  bool parse_error = false;
  auto it = decoder.field_int32(&parse_error);
  // The decoder doesn't return a parse error (maybe it should, but that has
  // been the behavior since the beginning).
  ASSERT_FALSE(parse_error);
  // But the iterator returns 0 elements.
  EXPECT_FALSE(it);
}

TEST(ProtoDecoderTest, RepeatedMaxFieldIdStack) {
  HeapBuffered<Message> message;
  message->AppendVarInt(15, 1);
  message->AppendVarInt(15, 2);
  std::vector<uint8_t> proto = message.SerializeAsArray();

  // Make sure that even with a max field id close to the stack capacity,
  // TypedProtoDecoder behaves correctly w.r.t. repeated fields.
  const int kMaxFieldId = PROTOZERO_DECODER_INITIAL_STACK_CAPACITY;

  {
    protozero::TypedProtoDecoder<kMaxFieldId,
                                 /*HAS_NONPACKED_REPEATED_FIELDS=*/true>
        decoder(proto.data(), proto.size());
    std::vector<uint64_t> res;
    for (auto it = decoder.GetRepeated<uint64_t>(15); it; it++) {
      res.push_back(*it);
    }
    EXPECT_THAT(res, ElementsAre(1, 2));
  }
}

}  // namespace
}  // namespace protozero
