blob: 362686d891a685ada3bf869570a2497dfa8b1554 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/update_client/op_download.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/check_deref.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "components/prefs/testing_pref_service.h"
#include "components/update_client/crx_downloader.h"
#include "components/update_client/crx_downloader_factory.h"
#include "components/update_client/persisted_data.h"
#include "components/update_client/test_configurator.h"
#include "components/update_client/update_engine.h"
#include "crypto/sha2.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace update_client {
namespace {
class FakeDownloader : public CrxDownloader {
public:
FakeDownloader(const base::FilePath& dest,
base::expected<std::string, int> result,
const CrxDownloader::DownloadMetrics& metrics)
: CrxDownloader(nullptr),
dest_(dest),
result_(result),
metrics_(metrics) {}
base::OnceClosure DoStartDownload(const GURL& url) override {
if (result_.has_value()) {
base::WriteFile(dest_, result_.value());
OnDownloadComplete(true, {.response = dest_}, metrics_);
} else {
OnDownloadComplete(true, {.error = result_.error()}, metrics_);
}
return base::DoNothing();
}
protected:
~FakeDownloader() override = default;
private:
const base::FilePath dest_;
const base::expected<std::string, int> result_;
const CrxDownloader::DownloadMetrics metrics_;
};
class FakeFactory : public CrxDownloaderFactory {
public:
FakeFactory(const base::FilePath dest,
base::expected<std::string, int> result,
const CrxDownloader::DownloadMetrics& metrics)
: dest_(dest), result_(result), metrics_(metrics) {}
scoped_refptr<CrxDownloader> MakeCrxDownloader(
bool background_download_enabled) const override {
return base::MakeRefCounted<FakeDownloader>(dest_, result_, metrics_);
}
protected:
~FakeFactory() override = default;
private:
const base::FilePath dest_;
const base::expected<std::string, int> result_;
const CrxDownloader::DownloadMetrics metrics_;
};
} // namespace
class OpDownloadTest : public testing::Test {
public:
OpDownloadTest() { RegisterPersistedDataPrefs(pref_->registry()); }
// Overrides from testing::Test.
void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
scoped_refptr<TestConfigurator> MakeConfigurator(
base::expected<std::string, int> result,
const CrxDownloader::DownloadMetrics& metrics) {
scoped_refptr<TestConfigurator> config =
base::MakeRefCounted<TestConfigurator>(pref_.get());
config->SetCrxDownloaderFactory(base::MakeRefCounted<FakeFactory>(
temp_dir_.GetPath().Append(FILE_PATH_LITERAL("OpDownloadTest_File")),
result, metrics));
return config;
}
CrxDownloader::ProgressCallback MakeProgressCallback() {
return base::DoNothing();
}
base::RepeatingCallback<void(base::Value::Dict)> MakePingCallback() {
return base::BindLambdaForTesting(
[&](base::Value::Dict ping) { pings_.push_back(std::move(ping)); });
}
base::OnceCallback<void(base::expected<base::FilePath, CategorizedError>)>
MakeDoneCallback() {
return base::BindLambdaForTesting(
[&](base::expected<base::FilePath, CategorizedError> outcome) {
outcome_ = outcome;
runloop_.Quit();
});
}
void Download(scoped_refptr<Configurator> config,
int64_t length,
const std::string& hash) {
DownloadOperation(config, "appid",
base::BindRepeating([](const base::FilePath&) -> int64_t {
return 100'000'000; // 100 MiB
}),
/*is_foreground=*/false, {GURL("http://localhost:111")},
length, hash, MakePingCallback(), base::DoNothing(),
MakeProgressCallback(), {}, MakeDoneCallback());
runloop_.Run();
}
base::test::TaskEnvironment task_environment_;
std::unique_ptr<TestingPrefServiceSimple> pref_ =
std::make_unique<TestingPrefServiceSimple>();
base::ScopedTempDir temp_dir_;
base::RunLoop runloop_;
std::vector<base::Value::Dict> pings_;
base::expected<base::FilePath, CategorizedError> outcome_;
};
TEST_F(OpDownloadTest, DownloadSuccess) {
const std::string data = "data";
Download(
MakeConfigurator(
data, {.url = GURL("http://test"),
.downloader =
CrxDownloader::DownloadMetrics::Downloader::kUrlFetcher,
.error = 0,
.extra_code1 = 34,
.downloaded_bytes = 56,
.total_bytes = 78,
.download_time_ms = 90}),
data.length(), base::HexEncode(crypto::SHA256HashString(data)));
EXPECT_TRUE(outcome_.has_value());
ASSERT_EQ(pings_.size(), 1u);
EXPECT_EQ(pings_[0].FindInt("eventtype"), 14);
EXPECT_EQ(pings_[0].FindInt("eventresult"), 1);
EXPECT_EQ(pings_[0].Find("errorcode"), nullptr);
EXPECT_EQ(pings_[0].FindInt("extracode1"), 34);
EXPECT_EQ(pings_[0].FindDouble("downloaded"), 56);
EXPECT_EQ(pings_[0].FindDouble("total"), 78);
EXPECT_EQ(pings_[0].FindDouble("download_time_ms"), 90);
EXPECT_EQ(CHECK_DEREF(pings_[0].FindString("downloader")), "direct");
}
TEST_F(OpDownloadTest, DownloadFailure) {
const std::string data = "data";
Download(MakeConfigurator(
base::unexpected(404),
{.url = GURL("http://test"),
.downloader =
CrxDownloader::DownloadMetrics::Downloader::kUrlFetcher,
.error = 404,
.extra_code1 = 34,
.downloaded_bytes = 56,
.total_bytes = 78,
.download_time_ms = 90}),
data.length(), base::HexEncode(crypto::SHA256HashString(data)));
EXPECT_FALSE(outcome_.has_value());
ASSERT_EQ(pings_.size(), 1u);
EXPECT_EQ(pings_[0].FindInt("eventtype"), 14);
EXPECT_EQ(pings_[0].FindInt("eventresult"), 0);
EXPECT_EQ(pings_[0].FindInt("errorcode"), 404);
EXPECT_EQ(pings_[0].FindInt("extracode1"), 34);
EXPECT_EQ(pings_[0].FindDouble("downloaded"), 56);
EXPECT_EQ(pings_[0].FindDouble("total"), 78);
EXPECT_EQ(pings_[0].FindDouble("download_time_ms"), 90);
EXPECT_EQ(CHECK_DEREF(pings_[0].FindString("downloader")), "direct");
}
} // namespace update_client