chromium/chrome/browser/ash/android_sms/fcm_connection_establisher_unittest.cc

// Copyright 2018 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/ash/android_sms/fcm_connection_establisher.h"

#include <utility>

#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/timer/mock_timer.h"
#include "chrome/browser/ash/android_sms/android_sms_urls.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/fake_service_worker_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/messaging/string_message_codec.h"
#include "third_party/blink/public/common/messaging/transferable_message.h"

namespace ash {
namespace android_sms {

class FcmConnectionEstablisherTest : public testing::Test {
 public:
  FcmConnectionEstablisherTest(const FcmConnectionEstablisherTest&) = delete;
  FcmConnectionEstablisherTest& operator=(const FcmConnectionEstablisherTest&) =
      delete;

 protected:
  FcmConnectionEstablisherTest()
      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
  ~FcmConnectionEstablisherTest() override = default;

  void VerifyTransferrableMessage(const char* expected,
                                  blink::TransferableMessage message) {
    auto payload = blink::DecodeToWebMessagePayload(std::move(message));
    EXPECT_EQ(base::UTF8ToUTF16(expected),
              absl::get<std::u16string>(payload.value()));
  }

 private:
  content::BrowserTaskEnvironment task_environment_;
};

TEST_F(FcmConnectionEstablisherTest, TestEstablishConnection) {
  auto mock_retry_timer = std::make_unique<base::MockOneShotTimer>();
  base::MockOneShotTimer* mock_retry_timer_ptr = mock_retry_timer.get();
  base::HistogramTester histogram_tester;

  content::FakeServiceWorkerContext fake_service_worker_context;
  FcmConnectionEstablisher fcm_connection_establisher(
      std::move(mock_retry_timer));
  auto& message_dispatch_calls =
      fake_service_worker_context
          .start_service_worker_and_dispatch_message_calls();

  // Verify that message is dispatch to service worker.
  fcm_connection_establisher.EstablishConnection(
      GetAndroidMessagesURL(),
      ConnectionEstablisher::ConnectionMode::kStartConnection,
      &fake_service_worker_context);
  base::RunLoop().RunUntilIdle();
  ASSERT_EQ(1u, message_dispatch_calls.size());
  EXPECT_EQ(GetAndroidMessagesURL(), std::get<GURL>(message_dispatch_calls[0]));
  VerifyTransferrableMessage(FcmConnectionEstablisher::kStartFcmMessage,
                             std::move(std::get<blink::TransferableMessage>(
                                 message_dispatch_calls[0])));

  // Return success to result callback and verify that no retries are attempted
  // and success histogram is recorded.
  std::move(std::get<content::ServiceWorkerContext::ResultCallback>(
                message_dispatch_calls[0]))
      .Run(true /* status */);
  ASSERT_EQ(1u, message_dispatch_calls.size());
  EXPECT_FALSE(mock_retry_timer_ptr->IsRunning());
  histogram_tester.ExpectBucketCount(
      "AndroidSms.FcmMessageDispatchSuccess",
      FcmConnectionEstablisher::MessageType::kStart, 1);

  // Verify that when multiple requests are sent only the first one is
  // dispatched while the others are queued.
  fcm_connection_establisher.EstablishConnection(
      GetAndroidMessagesURL(),
      ConnectionEstablisher::ConnectionMode::kStartConnection,
      &fake_service_worker_context);
  fcm_connection_establisher.EstablishConnection(
      GetAndroidMessagesURL(),
      ConnectionEstablisher::ConnectionMode::kResumeExistingConnection,
      &fake_service_worker_context);
  base::RunLoop().RunUntilIdle();
  ASSERT_EQ(2u, message_dispatch_calls.size());
  VerifyTransferrableMessage(FcmConnectionEstablisher::kStartFcmMessage,
                             std::move(std::get<blink::TransferableMessage>(
                                 message_dispatch_calls[1])));

  // Verify that if the first request fails then it's retried
  std::move(std::get<content::ServiceWorkerContext::ResultCallback>(
                message_dispatch_calls[1]))
      .Run(false /* status */);
  ASSERT_EQ(2u, message_dispatch_calls.size());
  EXPECT_TRUE(mock_retry_timer_ptr->IsRunning());
  // Retry shouldn't count success.
  histogram_tester.ExpectBucketCount(
      "AndroidSms.FcmMessageDispatchSuccess",
      FcmConnectionEstablisher::MessageType::kStart, 1);
  mock_retry_timer_ptr->Fire();
  ASSERT_EQ(3u, message_dispatch_calls.size());
  VerifyTransferrableMessage(FcmConnectionEstablisher::kStartFcmMessage,
                             std::move(std::get<blink::TransferableMessage>(
                                 message_dispatch_calls[2])));

  // Verify that if the first request succeeds then the next message is
  // dispatched
  std::move(std::get<content::ServiceWorkerContext::ResultCallback>(
                message_dispatch_calls[2]))
      .Run(true /* status */);
  ASSERT_EQ(4u, message_dispatch_calls.size());
  EXPECT_FALSE(mock_retry_timer_ptr->IsRunning());
  VerifyTransferrableMessage(FcmConnectionEstablisher::kResumeFcmMessage,
                             std::move(std::get<blink::TransferableMessage>(
                                 message_dispatch_calls[3])));

  // Complete second request and verify that no more retries are scheduled.
  std::move(std::get<content::ServiceWorkerContext::ResultCallback>(
                message_dispatch_calls[3]))
      .Run(true /* status */);
  EXPECT_FALSE(mock_retry_timer_ptr->IsRunning());

  // Verify that max retries are attempted before abandoning request
  fcm_connection_establisher.EstablishConnection(
      GetAndroidMessagesURL(),
      ConnectionEstablisher::ConnectionMode::kStartConnection,
      &fake_service_worker_context);
  base::RunLoop().RunUntilIdle();

  int last_retry_bucket_count = histogram_tester.GetBucketCount(
      "AndroidSms.FcmMessageDispatchRetry",
      static_cast<base::HistogramBase::Sample>(
          FcmConnectionEstablisher::MessageType::kStart));

  int retry_count = 0;
  while (true) {
    ASSERT_EQ(5u + retry_count, message_dispatch_calls.size());
    std::move(std::get<content::ServiceWorkerContext::ResultCallback>(
                  message_dispatch_calls[4 + retry_count]))
        .Run(false /* status */);
    if (mock_retry_timer_ptr->IsRunning()) {
      mock_retry_timer_ptr->Fire();
      retry_count++;
    } else {
      break;
    }
  }

  EXPECT_EQ(FcmConnectionEstablisher::kMaxRetryCount, retry_count);
  histogram_tester.ExpectBucketCount(
      "AndroidSms.FcmMessageDispatchRetry",
      FcmConnectionEstablisher::MessageType::kStart,
      retry_count + last_retry_bucket_count);
  histogram_tester.ExpectBucketCount(
      "AndroidSms.FcmMessageDispatchFailure",
      FcmConnectionEstablisher::MessageType::kStart, 1);
}

TEST_F(FcmConnectionEstablisherTest, TestTearDownConnection) {
  content::FakeServiceWorkerContext fake_service_worker_context;
  FcmConnectionEstablisher fcm_connection_establisher(
      std::make_unique<base::MockOneShotTimer>());
  auto& message_dispatch_calls =
      fake_service_worker_context
          .start_service_worker_and_dispatch_message_calls();

  // Verify that message is dispatch to service worker.
  fcm_connection_establisher.TearDownConnection(GetAndroidMessagesURL(),
                                                &fake_service_worker_context);
  base::RunLoop().RunUntilIdle();
  ASSERT_EQ(1u, message_dispatch_calls.size());
  EXPECT_EQ(GetAndroidMessagesURL(), std::get<GURL>(message_dispatch_calls[0]));
  VerifyTransferrableMessage(FcmConnectionEstablisher::kStopFcmMessage,
                             std::move(std::get<blink::TransferableMessage>(
                                 message_dispatch_calls[0])));
}

}  // namespace android_sms
}  // namespace ash