// Exercise concurrent serialization and deserialization.
TEST_F(PushDeserializerTest, SerializationDeserialization) {
  auto const trajectory = BuildTrajectory();
  int const byte_size = trajectory->ByteSize();
  for (int i = 0; i < kRunsPerTest; ++i) {
    auto read_trajectory = make_not_null_unique<Trajectory>();
    auto written_trajectory = BuildTrajectory();
    auto storage = std::make_unique<std::uint8_t[]>(byte_size);
    std::uint8_t* data = &storage[0];

    pull_serializer_ =
        std::make_unique<PullSerializer>(kSerializerChunkSize, kNumberOfChunks);
    push_deserializer_ = std::make_unique<PushDeserializer>(
        kDeserializerChunkSize, kNumberOfChunks);

    pull_serializer_->Start(std::move(written_trajectory));
    push_deserializer_->Start(
        std::move(read_trajectory), PushDeserializerTest::CheckSerialization);
    for (;;) {
      Bytes const bytes = pull_serializer_->Pull();
      std::memcpy(data, bytes.data, static_cast<size_t>(bytes.size));
      push_deserializer_->Push(Bytes(data, bytes.size),
                               std::bind(&PushDeserializerTest::Stomp,
                                         Bytes(data, bytes.size)));
      data = &data[bytes.size];
      if (bytes.size == 0) {
        break;
      }
    }

    // Destroying the deserializer waits until deserialization is done.  It is
    // important that this happens before |storage| is destroyed.
    pull_serializer_.reset();
    push_deserializer_.reset();
  }
}
TEST_F(PullSerializerTest, SerializationThreading) {
  Trajectory read_trajectory;
  auto const trajectory = BuildTrajectory();
  int const byte_size = trajectory->ByteSize();
  auto expected_serialized_trajectory =
      std::make_unique<std::uint8_t[]>(byte_size);
  trajectory->SerializePartialToArray(&expected_serialized_trajectory[0],
                                      byte_size);

  // Run this test repeatedly to detect threading issues (it will flake in case
  // of problems).
  for (int i = 0; i < kRunsPerTest; ++i) {
    auto trajectory = BuildTrajectory();
    auto actual_serialized_trajectory =
        std::make_unique<std::uint8_t[]>(byte_size);
    std::uint8_t* data = &actual_serialized_trajectory[0];

    // The serialization happens concurrently with the test.
    pull_serializer_ =
        std::make_unique<PullSerializer>(kChunkSize, kNumberOfChunks);
    pull_serializer_->Start(std::move(trajectory));
    for (;;) {
      Bytes const bytes = pull_serializer_->Pull();
      std::memcpy(data, bytes.data, static_cast<size_t>(bytes.size));
      data = &data[bytes.size];
      if (bytes.size == 0) {
        break;
      }
    }
    pull_serializer_.reset();

    // Check if the serialized version can be parsed and if not print the first
    // difference.
    if (!read_trajectory.ParseFromArray(&actual_serialized_trajectory[0],
                                        byte_size)) {
      for (int i = 0; i < byte_size; ++i) {
        if (expected_serialized_trajectory[i] !=
            actual_serialized_trajectory[i]) {
          LOG(FATAL) << "position=" << i
                     << ", expected="
                     << static_cast<int>(expected_serialized_trajectory[i])
                     << ", actual="
                     << static_cast<int>(actual_serialized_trajectory[i]);
        }
      }
    }
  }
}
TEST_F(PullSerializerTest, SerializationSizes) {
  auto trajectory = BuildTrajectory();
  pull_serializer_->Start(std::move(trajectory));
  std::vector<std::int64_t> actual_sizes;
  std::vector<std::int64_t> expected_sizes(53, kChunkSize);
  expected_sizes.push_back(53);
  for (;;) {
    Bytes const bytes = pull_serializer_->Pull();
    if (bytes.size == 0) {
      break;
    }
    actual_sizes.push_back(bytes.size);
  }
  EXPECT_THAT(actual_sizes, ElementsAreArray(expected_sizes));
}
      }
    }

    // Destroying the deserializer waits until deserialization is done.  It is
    // important that this happens before |storage| is destroyed.
    pull_serializer_.reset();
    push_deserializer_.reset();
  }
}

// Check that deserialization fails if we stomp on one extra bytes.
TEST_F(PushDeserializerDeathTest, Stomp) {
  EXPECT_DEATH({
    const int kStompChunk = 77;
    auto read_trajectory = make_not_null_unique<Trajectory>();
    auto const trajectory = BuildTrajectory();
    int const byte_size = trajectory->ByteSize();
    auto serialized_trajectory =
        std::make_unique<std::uint8_t[]>(byte_size);
    trajectory->SerializePartialToArray(&serialized_trajectory[0], byte_size);
    push_deserializer_->Start(
      std::move(read_trajectory), &PushDeserializerTest::CheckSerialization);
    int left = byte_size;
    for (int i = 0; i < byte_size; i += kStompChunk) {
      Bytes bytes(&serialized_trajectory[i], std::min(left, kStompChunk));
      push_deserializer_->Push(bytes,
                               std::bind(&PushDeserializerTest::Stomp,
                                         Bytes(bytes.data, bytes.size + 1)));
      left -= kStompChunk;
    }
    push_deserializer_->Push(Bytes(), nullptr);