chromium/chrome/browser/nearby_sharing/instantmessaging/stream_parser_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/nearby_sharing/instantmessaging/stream_parser.h"

#include <algorithm>
#include <string>
#include <string_view>
#include <vector>

#include "base/functional/bind.h"
#include "chrome/browser/nearby_sharing/instantmessaging/proto/instantmessaging.pb.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse
CreateReceiveMessagesResponse(const std::string& msg) {
  chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse
      response;
  response.mutable_inbox_message()->set_message(msg);
  return response;
}

chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse
CreateFastPathReadyResponse() {
  chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse
      response;
  response.mutable_fast_path_ready();
  return response;
}

chrome_browser_nearby_sharing_instantmessaging::StreamBody BuildProto(
    const std::vector<std::string>& messages,
    bool include_fast_path = false) {
  chrome_browser_nearby_sharing_instantmessaging::StreamBody stream_body;
  if (include_fast_path) {
    stream_body.add_messages(CreateFastPathReadyResponse().SerializeAsString());
  }
  for (const auto& msg : messages) {
    stream_body.add_messages(
        CreateReceiveMessagesResponse(msg).SerializeAsString());
  }
  return stream_body;
}

}  // namespace

class StreamParserTest : public testing::Test {
 public:
  StreamParserTest() = default;
  ~StreamParserTest() override = default;

  StreamParser& GetStreamParser() { return stream_parser_; }

 private:
  StreamParser stream_parser_;
};

// The entire message is sent in one response body.
TEST_F(StreamParserTest, SingleEntireMessageAtOnce) {
  std::vector<std::string> messages = {"random 42"};
  chrome_browser_nearby_sharing_instantmessaging::StreamBody stream_body =
      BuildProto(messages);
  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(stream_body.SerializeAsString());
  EXPECT_EQ(responses.size(), 1u);
  EXPECT_EQ(responses[0].mutable_inbox_message()->message(), messages[0]);
}

// More than one message is sent in one response body.
TEST_F(StreamParserTest, MultipleEntireMessagesAtOnce) {
  std::vector<std::string> messages = {"random 42", "more random 98",
                                       "helloworld 25"};
  chrome_browser_nearby_sharing_instantmessaging::StreamBody stream_body =
      BuildProto(messages);
  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(stream_body.SerializeAsString());

  EXPECT_EQ(responses.size(), 3u);
  EXPECT_EQ(responses[0].mutable_inbox_message()->message(), messages[0]);
  EXPECT_EQ(responses[1].mutable_inbox_message()->message(), messages[1]);
  EXPECT_EQ(responses[2].mutable_inbox_message()->message(), messages[2]);
}

// A single message is sent over multiple response bodies.
TEST_F(StreamParserTest, SingleMessageSplit) {
  std::vector<std::string> messages = {"random 42 and random 92"};
  chrome_browser_nearby_sharing_instantmessaging::StreamBody stream_body =
      BuildProto(messages);
  std::string serialized_msg = stream_body.SerializeAsString();

  // Randomly chosen.
  int pos = 13;

  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(serialized_msg.substr(0, pos));
  EXPECT_EQ(responses.size(), 0u);

  responses = GetStreamParser().Append(serialized_msg.substr(pos));
  EXPECT_EQ(responses.size(), 1u);
  EXPECT_EQ(responses[0].mutable_inbox_message()->message(), messages[0]);
}

// Multiple messages are sent over multiple response bodies.
TEST_F(StreamParserTest, MultipleMessagesSplit) {
  std::vector<std::string> messages_1 = {"The quick", "brown fox", "jumps"};
  std::vector<std::string> messages_2 = {"over the lazy", "dog."};

  std::string serialized_msg_1 = BuildProto(messages_1).SerializeAsString();
  std::string serialized_msg_2 = BuildProto(messages_2).SerializeAsString();

  // Randomly chosen.
  int pos = 7;

  std::string first_message =
      serialized_msg_1 + serialized_msg_2.substr(0, pos);
  std::string second_message = serialized_msg_2.substr(pos);

  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(first_message);
  EXPECT_EQ(3u, responses.size());
  EXPECT_EQ(messages_1[0], responses[0].mutable_inbox_message()->message());
  EXPECT_EQ(messages_1[1], responses[1].mutable_inbox_message()->message());
  EXPECT_EQ(messages_1[2], responses[2].mutable_inbox_message()->message());

  responses = GetStreamParser().Append(second_message);
  EXPECT_EQ(2u, responses.size());
  EXPECT_EQ(messages_2[0], responses[0].mutable_inbox_message()->message());
  EXPECT_EQ(messages_2[1], responses[1].mutable_inbox_message()->message());
}

// Check that the buffer resizes properly when a long message is sent at once.
TEST_F(StreamParserTest, LongMessageAtOnce) {
  std::vector<std::string> messages = {
      "This is a long test message to see if the buffer breaks if we send a "
      "big message: "
      "111111111111111111111111111111111111111111111111111111111111111111111111"
      "111111111111111111111111111111111111111111111111111111111111111111111111"
      "111111111111111111111111111111111111111111111111111111111111111111111111"
      "111111111111111111111111111111111111111111111111111111111111111111111111"
      "111111111111111111111111111111111111111111111111111111111111111111111111"
      "111111111111111111111111111111111111111111111111111111111111111111111111"
      "111111"};
  chrome_browser_nearby_sharing_instantmessaging::StreamBody stream_body =
      BuildProto(messages);
  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(stream_body.SerializeAsString());
  EXPECT_EQ(1u, responses.size());
  EXPECT_EQ(messages[0], responses[0].mutable_inbox_message()->message());
}

// Check that when we have a tag failure, no message is received.
TEST_F(StreamParserTest, TagFailure) {
  std::string message = "";
  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(message);
  EXPECT_EQ(0u, responses.size());

  char bytes[2] = {0x0f, 0x00};
  auto bytes_string = std::string_view(bytes);
  EXPECT_EQ(1u, bytes_string.length());
  responses = GetStreamParser().Append(bytes);
  EXPECT_EQ(0u, responses.size());
}

// Check that when we have a ReadBytes failure, no message is received.
TEST_F(StreamParserTest, ReadBytesFailure) {
  std::vector<std::string> messages = {"random 42 and random 92"};
  chrome_browser_nearby_sharing_instantmessaging::StreamBody stream_body =
      BuildProto(messages);
  std::string serialized_msg = stream_body.SerializeAsString();

  // Randomly chosen.
  int pos = 13;

  std::vector<
      chrome_browser_nearby_sharing_instantmessaging::ReceiveMessagesResponse>
      responses = GetStreamParser().Append(serialized_msg.substr(0, pos));
  EXPECT_EQ(responses.size(), 0u);
}