普通文本  |  242行  |  7.95 KB

/*
 * 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 "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/base/utils.h"
#include "perfetto/protozero/message.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/protozero/scattered_heap_buffer.h"

namespace protozero {
namespace {

using ::testing::_;
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, VeryLargeField) {
  const uint64_t size = 512 * 1024 * 1024 + 6;
  std::unique_ptr<uint8_t, perfetto::base::FreeDeleter> data(
      static_cast<uint8_t*>(malloc(size)));

  data.get()[0] = static_cast<unsigned char>('\x0A');
  data.get()[1] = static_cast<unsigned char>('\x80');
  data.get()[2] = static_cast<unsigned char>('\x80');
  data.get()[3] = static_cast<unsigned char>('\x80');
  data.get()[4] = static_cast<unsigned char>('\x80');
  data.get()[5] = static_cast<unsigned char>('\x02');

  ProtoDecoder decoder(data.get(), size);
  Field field = decoder.ReadField();
  ASSERT_EQ(1u, field.id());
  ASSERT_EQ(nullptr, field.data());
  ASSERT_EQ(0u, field.size());
  ASSERT_EQ(0u, decoder.bytes_left());
}

TEST(ProtoDecoderTest, SingleRepeatedField) {
  Message message;
  ScatteredHeapBuffer delegate(512, 512);
  ScatteredStreamWriter writer(&delegate);
  delegate.set_writer(&writer);
  message.Reset(&writer);
  message.AppendVarInt(/*field_id=*/2, 10);
  delegate.AdjustUsedSizeOfCurrentSlice();
  auto used_range = delegate.slices()[0].GetUsedRange();

  TypedProtoDecoder<2, true> tpd(used_range.begin, used_range.size());
  auto it = tpd.GetRepeated(/*field_id=*/2);
  EXPECT_TRUE(it);
  EXPECT_EQ(it->as_int32(), 10);
  EXPECT_FALSE(++it);
}

TEST(ProtoDecoderTest, SingleRepeatedFieldWithExpansion) {
  Message message;
  ScatteredHeapBuffer delegate(512, 512);
  ScatteredStreamWriter writer(&delegate);
  delegate.set_writer(&writer);
  message.Reset(&writer);
  for (int i = 0; i < 2000; i++) {
    message.AppendVarInt(/*field_id=*/2, i);
  }
  std::vector<uint8_t> data = delegate.StitchSlices();

  TypedProtoDecoder<2, true> tpd(data.data(), data.size());
  auto it = tpd.GetRepeated(/*field_id=*/2);
  for (int i = 0; i < 2000; i++) {
    EXPECT_TRUE(it);
    EXPECT_EQ(it->as_int32(), i);
    ++it;
  }
  EXPECT_FALSE(it);
}

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

TEST(ProtoDecoderTest, RepeatedFields) {
  Message message;
  ScatteredHeapBuffer delegate(512, 512);
  ScatteredStreamWriter writer(&delegate);
  delegate.set_writer(&writer);
  message.Reset(&writer);

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

  message.AppendVarInt(1, 11);
  message.AppendVarInt(2, 21);
  message.AppendVarInt(2, 22);

  delegate.AdjustUsedSizeOfCurrentSlice();
  auto used_range = delegate.slices()[0].GetUsedRange();

  // When iterating with the simple decoder we should just see fields in parsing
  // order.
  ProtoDecoder decoder(used_range.begin, used_range.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(used_range.begin, used_range.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(1);
  EXPECT_EQ(it->as_int32(), 10);
  EXPECT_EQ((++it)->as_int32(), 11);
  EXPECT_FALSE(++it);

  it = tpd.GetRepeated(2);
  EXPECT_EQ(it->as_int32(), 20);
  EXPECT_EQ((++it)->as_int32(), 21);
  EXPECT_EQ((++it)->as_int32(), 22);
  EXPECT_FALSE(++it);

  it = tpd.GetRepeated(3);
  EXPECT_EQ(it->as_int32(), 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, 0> 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);
}

}  // namespace
}  // namespace protozero