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

#include "extensions/browser/updater/extension_downloader_test_helper.h"

#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "extensions/common/verifier_formats.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"

using testing::_;
using testing::Invoke;

namespace extensions {

const net::BackoffEntry::Policy kZeroBackoffPolicy = {
    // Number of initial errors (in sequence) to ignore before applying
    // exponential back-off rules.
    0,

    // Initial delay for exponential back-off in ms.
    0,

    // 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.1,

    // Maximum amount of time we are willing to delay our request in ms.
    600000,  // Ten 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,
};

MockExtensionDownloaderDelegate::MockExtensionDownloaderDelegate() = default;

MockExtensionDownloaderDelegate::~MockExtensionDownloaderDelegate() = default;

void MockExtensionDownloaderDelegate::Wait() {
  std::unique_ptr<base::RunLoop> runner = std::make_unique<base::RunLoop>();
  quit_closure_ = runner->QuitClosure();
  runner->Run();
  quit_closure_.Reset();
}

void MockExtensionDownloaderDelegate::Quit() {
  quit_closure_.Run();
}

void MockExtensionDownloaderDelegate::DelegateTo(
    ExtensionDownloaderDelegate* delegate) {
  ON_CALL(*this, OnExtensionDownloadFailed(_, _, _, _, _))
      .WillByDefault(Invoke(
          delegate, &ExtensionDownloaderDelegate::OnExtensionDownloadFailed));
  ON_CALL(*this, OnExtensionDownloadStageChanged(_, _))
      .WillByDefault(Invoke(
          delegate,
          &ExtensionDownloaderDelegate::OnExtensionDownloadStageChanged));
  ON_CALL(*this, OnExtensionDownloadFinished_(_, _, _, _, _, _))
      .WillByDefault(
          [delegate](const CRXFileInfo& file, bool file_ownership_passed,
                     const GURL& download_url, const PingResult& ping_result,
                     const std::set<int>& request_ids,
                     InstallCallback& callback) {
            delegate->OnExtensionDownloadFinished(
                file, file_ownership_passed, download_url, ping_result,
                request_ids, std::move(callback));
          });
  ON_CALL(*this, OnExtensionDownloadRetryForTests())
      .WillByDefault(Invoke(
          delegate,
          &ExtensionDownloaderDelegate::OnExtensionDownloadRetryForTests));
  ON_CALL(*this, GetPingDataForExtension(_, _))
      .WillByDefault(Invoke(
          delegate, &ExtensionDownloaderDelegate::GetPingDataForExtension));
  ON_CALL(*this, IsExtensionPending(_))
      .WillByDefault(
          Invoke(delegate, &ExtensionDownloaderDelegate::IsExtensionPending));
  ON_CALL(*this, GetExtensionExistingVersion(_, _))
      .WillByDefault(Invoke(
          delegate, &ExtensionDownloaderDelegate::GetExtensionExistingVersion));
}

MockExtensionCache::MockExtensionCache() = default;
MockExtensionCache::~MockExtensionCache() = default;

void MockExtensionCache::Start(base::OnceClosure callback) {
  std::move(callback).Run();
}

void MockExtensionCache::Shutdown(base::OnceClosure callback) {
  std::move(callback).Run();
}

ExtensionDownloaderTestHelper::ExtensionDownloaderTestHelper()
    : test_shared_url_loader_factory_(
          base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
              &test_url_loader_factory_)),
      delegate_(),
      downloader_(&delegate_,
                  test_shared_url_loader_factory_,
                  GetTestVerifierFormat()) {}

ExtensionDownloaderTestHelper::~ExtensionDownloaderTestHelper() = default;

void ExtensionDownloaderTestHelper::StartUpdateCheck(
    std::unique_ptr<ManifestFetchData> fetch_data) {
  downloader_.StartUpdateCheck(std::move(fetch_data));
}

network::TestURLLoaderFactory::PendingRequest*
ExtensionDownloaderTestHelper::GetPendingRequest(size_t index) {
  if (index >= test_url_loader_factory_.pending_requests()->size()) {
    return nullptr;
  }
  return &(*test_url_loader_factory_.pending_requests())[index];
}

void ExtensionDownloaderTestHelper::ClearURLLoaderFactoryResponses() {
  test_url_loader_factory_.ClearResponses();
}

std::unique_ptr<ExtensionDownloader>
ExtensionDownloaderTestHelper::CreateDownloader() {
  return std::make_unique<ExtensionDownloader>(
      &delegate_, test_shared_url_loader_factory_, GetTestVerifierFormat());
}

const DownloadPingData* ExtensionDownloaderTestHelper::GetTestPingData() {
  static const DownloadPingData kNeverPingedData =
      DownloadPingData(/*rollcall=*/ManifestFetchData::kNeverPinged,
                       /*active=*/ManifestFetchData::kNeverPinged,
                       /*enabled=*/true,
                       /*disable_reasons=*/{});
  return &kNeverPingedData;
}

ExtensionDownloaderTask CreateDownloaderTask(const ExtensionId& id,
                                             const GURL& update_url) {
  return ExtensionDownloaderTask(
      id, update_url, mojom::ManifestLocation::kInternal,
      false /* is_corrupt_reinstall */, 0 /* request_id */,
      DownloadFetchPriority::kBackground);
}

void AddExtensionToFetchDataForTesting(ManifestFetchData* fetch_data,
                                       const ExtensionId& id,
                                       const std::string& version,
                                       const GURL& update_url,
                                       const DownloadPingData& ping_data) {
  fetch_data->AddExtension(id, version, &ping_data,
                           ExtensionDownloaderTestHelper::kEmptyUpdateUrlData,
                           std::string(), mojom::ManifestLocation::kInternal,
                           DownloadFetchPriority::kBackground);
  fetch_data->AddAssociatedTask(CreateDownloaderTask(id, update_url));
}

void AddExtensionToFetchDataForTesting(ManifestFetchData* fetch_data,
                                       const ExtensionId& id,
                                       const std::string& version,
                                       const GURL& update_url) {
  AddExtensionToFetchDataForTesting(
      fetch_data, id, version, update_url,
      *ExtensionDownloaderTestHelper::GetTestPingData());
}

UpdateManifestItem::UpdateManifestItem(ExtensionId id) : id(std::move(id)) {}
UpdateManifestItem::~UpdateManifestItem() = default;
UpdateManifestItem::UpdateManifestItem(const UpdateManifestItem&) = default;
UpdateManifestItem& UpdateManifestItem::operator=(const UpdateManifestItem&) =
    default;
UpdateManifestItem::UpdateManifestItem(UpdateManifestItem&&) = default;
UpdateManifestItem& UpdateManifestItem::operator=(UpdateManifestItem&&) =
    default;

std::string CreateUpdateManifest(
    const std::vector<UpdateManifestItem>& extensions) {
  std::string content =
      "<?xml version='1.0' encoding='UTF-8'?>"
      "<gupdate xmlns='http://www.google.com/update2/response'"
      "                protocol='2.0'>";
  for (const auto& update_item : extensions) {
    content += base::StringPrintf(
        " <app appid='%s'>"
        "  <updatecheck",
        update_item.id.c_str());
    for (const auto& [name, value] : update_item.updatecheck_params) {
      content += base::StringPrintf(" %s='%s'", name.c_str(), value.c_str());
    }
    content +=
        " />"
        " </app>";
  }
  content += "</gupdate>";
  return content;
}

}  // namespace extensions
