/*
 * Copyright (C) 2017 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 "src/ipc/buffered_frame_deserializer.h"

#include <algorithm>
#include <string>

#include "gtest/gtest.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/utils.h"

#include "src/ipc/wire_protocol.pb.h"

namespace perfetto {
namespace ipc {
namespace {

constexpr size_t kHeaderSize = sizeof(uint32_t);

// Generates a parsable Frame of exactly |size| bytes (including header).
std::vector<char> GetSimpleFrame(size_t size) {
  // A bit of reverse math of the proto encoding: a Frame which has only the
  // |data_for_testing| fields, will require for each data_for_testing that is
  // up to 127 bytes:
  // - 1 byte to write the field preamble (field type and id).
  // - 1 byte to write the field size, if 0 < size <= 127.
  // - N bytes for the actual content (|padding| below).
  // So below we split the payload into chunks of <= 127 bytes, keeping into
  // account the extra 2 bytes for each chunk.
  Frame frame;
  std::vector<char> padding;
  char padding_char = '0';
  const size_t payload_size = size - kHeaderSize;
  for (size_t size_left = payload_size; size_left > 0;) {
    PERFETTO_CHECK(size_left >= 2);  // We cannot produce frames < 2 bytes.
    size_t padding_size;
    if (size_left <= 127) {
      padding_size = size_left - 2;
      size_left = 0;
    } else {
      padding_size = 124;
      size_left -= padding_size + 2;
    }
    padding.resize(padding_size);
    for (size_t i = 0; i < padding_size; i++) {
      padding_char = padding_char == 'z' ? '0' : padding_char + 1;
      padding[i] = padding_char;
    }
    frame.add_data_for_testing(padding.data(), padding_size);
  }
  PERFETTO_CHECK(frame.ByteSize() == static_cast<int>(payload_size));
  std::vector<char> encoded_frame;
  encoded_frame.resize(size);
  char* enc_buf = encoded_frame.data();
  PERFETTO_CHECK(frame.SerializeToArray(enc_buf + kHeaderSize, payload_size));
  memcpy(enc_buf, base::AssumeLittleEndian(&payload_size), kHeaderSize);
  PERFETTO_CHECK(encoded_frame.size() == size);
  return encoded_frame;
}

void CheckedMemcpy(BufferedFrameDeserializer::ReceiveBuffer rbuf,
                   const std::vector<char>& encoded_frame,
                   size_t offset = 0) {
  ASSERT_GE(rbuf.size, encoded_frame.size() + offset);
  memcpy(rbuf.data + offset, encoded_frame.data(), encoded_frame.size());
}

bool FrameEq(std::vector<char> expected_frame_with_header, const Frame& frame) {
  std::string reserialized_frame = frame.SerializeAsString();

  size_t expected_size = expected_frame_with_header.size() - kHeaderSize;
  EXPECT_EQ(expected_size, reserialized_frame.size());
  if (expected_size != reserialized_frame.size())
    return false;

  return memcmp(reserialized_frame.data(),
                expected_frame_with_header.data() + kHeaderSize,
                reserialized_frame.size()) == 0;
}

// Tests the simple case where each recv() just returns one whole header+frame.
TEST(BufferedFrameDeserializerTest, WholeMessages) {
  BufferedFrameDeserializer bfd;
  for (int i = 1; i <= 50; i++) {
    const size_t size = i * 10;
    BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();

    ASSERT_NE(nullptr, rbuf.data);
    std::vector<char> frame = GetSimpleFrame(size);
    CheckedMemcpy(rbuf, frame);
    ASSERT_TRUE(bfd.EndReceive(frame.size()));

    // Excactly one frame should be decoded, with no leftover buffer.
    auto decoded_frame = bfd.PopNextFrame();
    ASSERT_TRUE(decoded_frame);
    ASSERT_EQ(static_cast<int32_t>(size - kHeaderSize),
              decoded_frame->ByteSize());
    ASSERT_FALSE(bfd.PopNextFrame());
    ASSERT_EQ(0u, bfd.size());
  }
}

// Sends first a simple test frame. Then creates a realistic Frame fragmenting
// it in three chunks and tests that the decoded Frame matches the original one.
// The recv() sequence is as follows:
// 1. [ simple_frame ] [ frame_chunk1 ... ]
// 2. [ ... frame_chunk2 ... ]
// 3. [ ... frame_chunk3 ]
TEST(BufferedFrameDeserializerTest, FragmentedFrameIsCorrectlyDeserialized) {
  BufferedFrameDeserializer bfd;
  Frame frame;
  frame.set_request_id(42);
  auto* bind_reply = frame.mutable_msg_bind_service_reply();
  bind_reply->set_success(true);
  bind_reply->set_service_id(0x4242);
  auto* method = bind_reply->add_methods();
  method->set_id(0x424242);
  method->set_name("foo");
  std::vector<char> serialized_frame;
  uint32_t payload_size = frame.ByteSize();

  serialized_frame.resize(kHeaderSize + payload_size);
  ASSERT_TRUE(frame.SerializeToArray(serialized_frame.data() + kHeaderSize,
                                     payload_size));
  memcpy(serialized_frame.data(), base::AssumeLittleEndian(&payload_size),
         kHeaderSize);

  std::vector<char> simple_frame = GetSimpleFrame(32);
  std::vector<char> frame_chunk1(serialized_frame.begin(),
                                 serialized_frame.begin() + 5);
  BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
  CheckedMemcpy(rbuf, simple_frame);
  CheckedMemcpy(rbuf, frame_chunk1, simple_frame.size());
  ASSERT_TRUE(bfd.EndReceive(simple_frame.size() + frame_chunk1.size()));

  std::vector<char> frame_chunk2(serialized_frame.begin() + 5,
                                 serialized_frame.begin() + 10);
  rbuf = bfd.BeginReceive();
  CheckedMemcpy(rbuf, frame_chunk2);
  ASSERT_TRUE(bfd.EndReceive(frame_chunk2.size()));

  std::vector<char> frame_chunk3(serialized_frame.begin() + 10,
                                 serialized_frame.end());
  rbuf = bfd.BeginReceive();
  CheckedMemcpy(rbuf, frame_chunk3);
  ASSERT_TRUE(bfd.EndReceive(frame_chunk3.size()));

  // Validate the received frame2.
  std::unique_ptr<Frame> decoded_simple_frame = bfd.PopNextFrame();
  ASSERT_TRUE(decoded_simple_frame);
  ASSERT_EQ(static_cast<int32_t>(simple_frame.size() - kHeaderSize),
            decoded_simple_frame->ByteSize());

  std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame();
  ASSERT_TRUE(decoded_frame);
  ASSERT_TRUE(FrameEq(serialized_frame, *decoded_frame));
}

// Tests the case of a EndReceive(0) while receiving a valid frame in chunks.
TEST(BufferedFrameDeserializerTest, ZeroSizedReceive) {
  BufferedFrameDeserializer bfd;
  std::vector<char> frame = GetSimpleFrame(100);
  std::vector<char> frame_chunk1(frame.begin(), frame.begin() + 50);
  std::vector<char> frame_chunk2(frame.begin() + 50, frame.end());

  BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
  CheckedMemcpy(rbuf, frame_chunk1);
  ASSERT_TRUE(bfd.EndReceive(frame_chunk1.size()));

  rbuf = bfd.BeginReceive();
  ASSERT_TRUE(bfd.EndReceive(0));

  rbuf = bfd.BeginReceive();
  CheckedMemcpy(rbuf, frame_chunk2);
  ASSERT_TRUE(bfd.EndReceive(frame_chunk2.size()));

  // Excactly one frame should be decoded, with no leftover buffer.
  std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame();
  ASSERT_TRUE(decoded_frame);
  ASSERT_TRUE(FrameEq(frame, *decoded_frame));
  ASSERT_FALSE(bfd.PopNextFrame());
  ASSERT_EQ(0u, bfd.size());
}

// Tests the case of a EndReceive(4) where the header has no payload. The frame
// should be just skipped and not returned by PopNextFrame().
TEST(BufferedFrameDeserializerTest, EmptyPayload) {
  BufferedFrameDeserializer bfd;
  std::vector<char> frame = GetSimpleFrame(100);

  BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
  std::vector<char> empty_frame(kHeaderSize, 0);
  CheckedMemcpy(rbuf, empty_frame);
  ASSERT_TRUE(bfd.EndReceive(kHeaderSize));

  rbuf = bfd.BeginReceive();
  CheckedMemcpy(rbuf, frame);
  ASSERT_TRUE(bfd.EndReceive(frame.size()));

  // |fram| should be properly decoded.
  std::unique_ptr<Frame> decoded_frame = bfd.PopNextFrame();
  ASSERT_TRUE(decoded_frame);
  ASSERT_TRUE(FrameEq(frame, *decoded_frame));
  ASSERT_FALSE(bfd.PopNextFrame());
}

// Test the case where a single Receive() returns batches of > 1 whole frames.
// See case C in the comments for BufferedFrameDeserializer::EndReceive().
TEST(BufferedFrameDeserializerTest, MultipleFramesInOneReceive) {
  BufferedFrameDeserializer bfd;
  std::vector<std::vector<size_t>> frame_batch_sizes(
      {{11}, {13, 17, 19}, {23}, {29, 31}});

  for (std::vector<size_t>& batch : frame_batch_sizes) {
    BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
    size_t frame_offset_in_batch = 0;
    for (size_t frame_size : batch) {
      auto frame = GetSimpleFrame(frame_size);
      CheckedMemcpy(rbuf, frame, frame_offset_in_batch);
      frame_offset_in_batch += frame.size();
    }
    ASSERT_TRUE(bfd.EndReceive(frame_offset_in_batch));
    for (size_t expected_size : batch) {
      auto frame = bfd.PopNextFrame();
      ASSERT_TRUE(frame);
      ASSERT_EQ(static_cast<int32_t>(expected_size - kHeaderSize),
                frame->ByteSize());
    }
    ASSERT_FALSE(bfd.PopNextFrame());
    ASSERT_EQ(0u, bfd.size());
  }
}

TEST(BufferedFrameDeserializerTest, RejectVeryLargeFrames) {
  BufferedFrameDeserializer bfd;
  BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
  const uint32_t kBigSize = std::numeric_limits<uint32_t>::max() - 2;
  memcpy(rbuf.data, base::AssumeLittleEndian(&kBigSize), kHeaderSize);
  memcpy(rbuf.data + kHeaderSize, "some initial payload", 20);
  ASSERT_FALSE(bfd.EndReceive(kHeaderSize + 20));
}

// Tests the extreme case of recv() fragmentation. Two valid frames are received
// but each recv() puts one byte at a time. Covers cases A and B commented in
// BufferedFrameDeserializer::EndReceive().
TEST(BufferedFrameDeserializerTest, HighlyFragmentedFrames) {
  BufferedFrameDeserializer bfd;
  for (int i = 1; i <= 50; i++) {
    std::vector<char> frame = GetSimpleFrame(i * 100);
    for (size_t off = 0; off < frame.size(); off++) {
      BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
      CheckedMemcpy(rbuf, {frame[off]});

      // The frame should be available only when receiving the last byte.
      ASSERT_TRUE(bfd.EndReceive(1));
      if (off < frame.size() - 1) {
        ASSERT_FALSE(bfd.PopNextFrame()) << off << "/" << frame.size();
        ASSERT_EQ(off + 1, bfd.size());
      } else {
        ASSERT_TRUE(bfd.PopNextFrame());
      }
    }
  }
}

// A bunch of valid frames interleaved with frames that have a valid header
// but unparsable payload. The expectation is that PopNextFrame() returns
// nullptr for the unparsable frames but the other frames are decoded peroperly.
TEST(BufferedFrameDeserializerTest, CanRecoverAfterUnparsableFrames) {
  BufferedFrameDeserializer bfd;
  for (int i = 1; i <= 50; i++) {
    const size_t size = i * 10;
    std::vector<char> frame = GetSimpleFrame(size);
    const bool unparsable = (i % 3) == 1;
    if (unparsable)
      memset(frame.data() + kHeaderSize, 0xFF, size - kHeaderSize);

    BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
    CheckedMemcpy(rbuf, frame);
    ASSERT_TRUE(bfd.EndReceive(frame.size()));

    // Excactly one frame should be decoded if |parsable|. In any case no
    // leftover bytes should be left in the buffer.
    auto decoded_frame = bfd.PopNextFrame();
    if (unparsable) {
      ASSERT_FALSE(decoded_frame);
    } else {
      ASSERT_TRUE(decoded_frame);
      ASSERT_EQ(static_cast<int32_t>(size - kHeaderSize),
                decoded_frame->ByteSize());
    }
    ASSERT_EQ(0u, bfd.size());
  }
}

// Test that we can sustain recvs() which constantly max out the capacity.
// It sets up four frames:
// |frame1|: small, 1024 + 4 bytes.
// |frame2|: as big as the |kMaxCapacity|. Its recv() is split into two chunks.
// |frame3|: together with the 2nd part of |frame2| it maxes out capacity again.
// |frame4|: as big as the |kMaxCapacity|. Received in one recv(), no splits.
//
// Which are then recv()'d in a loop in the following way.
//    |------------ max recv capacity ------------|
// 1. [       frame1      ] [ frame2_chunk1 ..... ]
// 2. [ ... frame2_chunk2 ]
// 3. [  frame3  ]
// 4. [               frame 4                     ]
TEST(BufferedFrameDeserializerTest, FillCapacity) {
  size_t kMaxCapacity = 1024 * 16;
  BufferedFrameDeserializer bfd(kMaxCapacity);

  for (int i = 0; i < 3; i++) {
    std::vector<char> frame1 = GetSimpleFrame(1024);
    std::vector<char> frame2 = GetSimpleFrame(kMaxCapacity);
    std::vector<char> frame2_chunk1(
        frame2.begin(), frame2.begin() + kMaxCapacity - frame1.size());
    std::vector<char> frame2_chunk2(frame2.begin() + frame2_chunk1.size(),
                                    frame2.end());
    std::vector<char> frame3 =
        GetSimpleFrame(kMaxCapacity - frame2_chunk2.size());
    std::vector<char> frame4 = GetSimpleFrame(kMaxCapacity);
    ASSERT_EQ(kMaxCapacity, frame1.size() + frame2_chunk1.size());
    ASSERT_EQ(kMaxCapacity, frame2_chunk1.size() + frame2_chunk2.size());
    ASSERT_EQ(kMaxCapacity, frame2_chunk2.size() + frame3.size());
    ASSERT_EQ(kMaxCapacity, frame4.size());

    BufferedFrameDeserializer::ReceiveBuffer rbuf = bfd.BeginReceive();
    CheckedMemcpy(rbuf, frame1);
    CheckedMemcpy(rbuf, frame2_chunk1, frame1.size());
    ASSERT_TRUE(bfd.EndReceive(frame1.size() + frame2_chunk1.size()));

    rbuf = bfd.BeginReceive();
    CheckedMemcpy(rbuf, frame2_chunk2);
    ASSERT_TRUE(bfd.EndReceive(frame2_chunk2.size()));

    rbuf = bfd.BeginReceive();
    CheckedMemcpy(rbuf, frame3);
    ASSERT_TRUE(bfd.EndReceive(frame3.size()));

    rbuf = bfd.BeginReceive();
    CheckedMemcpy(rbuf, frame4);
    ASSERT_TRUE(bfd.EndReceive(frame4.size()));

    std::unique_ptr<Frame> decoded_frame_1 = bfd.PopNextFrame();
    ASSERT_TRUE(decoded_frame_1);
    ASSERT_TRUE(FrameEq(frame1, *decoded_frame_1));

    std::unique_ptr<Frame> decoded_frame_2 = bfd.PopNextFrame();
    ASSERT_TRUE(decoded_frame_2);
    ASSERT_TRUE(FrameEq(frame2, *decoded_frame_2));

    std::unique_ptr<Frame> decoded_frame_3 = bfd.PopNextFrame();
    ASSERT_TRUE(decoded_frame_3);
    ASSERT_TRUE(FrameEq(frame3, *decoded_frame_3));

    std::unique_ptr<Frame> decoded_frame_4 = bfd.PopNextFrame();
    ASSERT_TRUE(decoded_frame_4);
    ASSERT_TRUE(FrameEq(frame4, *decoded_frame_4));

    ASSERT_FALSE(bfd.PopNextFrame());
  }
}

}  // namespace
}  // namespace ipc
}  // namespace perfetto
