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

#include "google_apis/gcm/engine/gcm_request_test_base.h"

#include <cmath>

#include "base/strings/string_tokenizer.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"

namespace {

// Backoff policy for testing registration request.
const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
  // Number of initial errors (in sequence) to ignore before applying
  // exponential back-off rules.
  0,

  // Initial delay for exponential back-off in ms.
  15000,  // 15 seconds.

  // Factor by which the waiting time will be multiplied.
  2,

  // Fuzzing percentage. ex: 10% will spread requests randomly
  // between 90%-100% of the calculated time.
  0.5,  // 50%.

  // Maximum amount of time we are willing to delay our request in ms.
  1000 * 60 * 5, // 5 minutes.

  // Time to keep an entry from being discarded even when it
  // has no significant state, -1 to never discard.
  -1,

  // Don't use initial delay unless the last request was an error.
  false,
};

const network::TestURLLoaderFactory::PendingRequest* PendingForURL(
    network::TestURLLoaderFactory* factory,
    const std::string& url) {
  GURL gurl(url);
  std::vector<network::TestURLLoaderFactory::PendingRequest>* pending =
      factory->pending_requests();
  for (const auto& pending_request : *pending) {
    if (pending_request.request.url == gurl)
      return &pending_request;
  }
  return nullptr;
}

}  // namespace

namespace gcm {

GCMRequestTestBase::GCMRequestTestBase()
    : shared_factory_(
          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
              &test_url_loader_factory_)),
      retry_count_(0) {}

GCMRequestTestBase::~GCMRequestTestBase() {
}

const net::BackoffEntry::Policy& GCMRequestTestBase::GetBackoffPolicy() const {
  return kDefaultBackoffPolicy;
}

void GCMRequestTestBase::OnAboutToCompleteFetch() {}

void GCMRequestTestBase::SetResponseForURLAndComplete(
    const std::string& url,
    net::HttpStatusCode status_code,
    const std::string& response_body,
    int net_error_code) {
  if (retry_count_++)
    FastForwardToTriggerNextRetry();

  OnAboutToCompleteFetch();
  EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest(
      GURL(url), network::URLLoaderCompletionStatus(net_error_code),
      network::CreateURLResponseHead(status_code), response_body));
}

const net::HttpRequestHeaders* GCMRequestTestBase::GetExtraHeadersForURL(
    const std::string& url) {
  const network::TestURLLoaderFactory::PendingRequest* pending_request =
      PendingForURL(&test_url_loader_factory_, url);
  return pending_request ? &pending_request->request.headers : nullptr;
}

bool GCMRequestTestBase::GetUploadDataForURL(const std::string& url,
                                             std::string* data_out) {
  const network::TestURLLoaderFactory::PendingRequest* pending_request =
      PendingForURL(&test_url_loader_factory_, url);
  if (!pending_request)
    return false;
  *data_out = network::GetUploadData(pending_request->request);
  return true;
}

void GCMRequestTestBase::VerifyFetcherUploadDataForURL(
    const std::string& url,
    std::map<std::string, std::string>* expected_pairs) {
  std::string upload_data;
  ASSERT_TRUE(GetUploadDataForURL(url, &upload_data));

  // Verify data was formatted properly.
  base::StringTokenizer data_tokenizer(upload_data, "&=");
  while (data_tokenizer.GetNext()) {
    auto iter = expected_pairs->find(data_tokenizer.token());
    ASSERT_TRUE(iter != expected_pairs->end()) << data_tokenizer.token();
    ASSERT_TRUE(data_tokenizer.GetNext()) << data_tokenizer.token();
    ASSERT_EQ(iter->second, data_tokenizer.token());
    // Ensure that none of the keys appears twice.
    expected_pairs->erase(iter);
  }

  ASSERT_EQ(0UL, expected_pairs->size());
}

void GCMRequestTestBase::FastForwardToTriggerNextRetry() {
  // Here we compute the maximum delay time by skipping the jitter fluctuation
  // that only affects in the negative way.
  int next_retry_delay_ms = kDefaultBackoffPolicy.initial_delay_ms;
  next_retry_delay_ms *=
      pow(kDefaultBackoffPolicy.multiply_factor, retry_count_);
  task_environment_.FastForwardBy(base::Milliseconds(next_retry_delay_ms));
}

}  // namespace gcm