blob: fc37461fe214c7feafb208e5ce1de9120f711150 [file] [log] [blame]
// Copyright 2022 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/crx_cache.h"
#include <map>
#include <memory>
#include <string>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/types/expected.h"
#include "components/update_client/test_utils.h"
#include "components/update_client/update_client_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace update_client {
namespace {
auto ExpectPathExists() {
return base::BindOnce(
[](base::expected<base::FilePath, UnpackerError> result) {
ASSERT_TRUE(result.has_value()) << static_cast<int>(result.error());
EXPECT_TRUE(base::PathExists(result.value())) << result.value().value();
});
}
auto ExpectError(UnpackerError error) {
return base::BindOnce(
[](UnpackerError error,
base::expected<base::FilePath, UnpackerError> result) {
ASSERT_FALSE(result.has_value()) << result.value();
EXPECT_EQ(result.error(), error);
},
error);
}
auto ExpectHashes(
const std::multimap<std::string, std::string>& expected_hashes) {
return base::BindOnce(
[](const std::multimap<std::string, std::string>& expected_hashes,
const std::multimap<std::string, std::string>& result) {
EXPECT_EQ(result, expected_hashes);
},
expected_hashes);
}
} // namespace
class CrxCacheTest : public testing::Test {
protected:
void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
base::FilePath TempPath(const std::string& basename) {
return temp_dir_.GetPath().AppendUTF8(basename);
}
base::FilePath MakeFile() {
base::FilePath out;
EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &out));
return out;
}
void RunLoop() {
loop_->Run();
loop_ = std::make_unique<base::RunLoop>();
}
base::RepeatingClosure Quit() { return loop_->QuitClosure(); }
private:
base::ScopedTempDir temp_dir_;
base::test::TaskEnvironment env_;
std::unique_ptr<base::RunLoop> loop_ = std::make_unique<base::RunLoop>();
};
TEST_F(CrxCacheTest, PutGet) {
scoped_refptr<CrxCache> cache =
base::MakeRefCounted<CrxCache>(TempPath("cache_dir"));
cache->Put(MakeFile(), "appid", "hash", "fp", ExpectPathExists());
cache->GetByFp("fp", ExpectPathExists());
cache->GetByHash("hash", ExpectPathExists().Then(Quit()));
RunLoop();
}
TEST_F(CrxCacheTest, GetMissing) {
scoped_refptr<CrxCache> cache =
base::MakeRefCounted<CrxCache>(TempPath("cache_dir"));
cache->Put(MakeFile(), "appid", "hash", "fp", ExpectPathExists());
cache->GetByFp("fp2", ExpectError(UnpackerError::kCrxCacheFileNotCached));
cache->GetByHash(
"hash2", ExpectError(UnpackerError::kCrxCacheFileNotCached).Then(Quit()));
RunLoop();
}
TEST_F(CrxCacheTest, PutReplacesByAppId) {
scoped_refptr<CrxCache> cache =
base::MakeRefCounted<CrxCache>(TempPath("cache_dir"));
cache->Put(MakeFile(), "appid", "hash", "fp", ExpectPathExists());
cache->GetByHash("hash", ExpectPathExists().Then(Quit()));
RunLoop();
cache->Put(MakeFile(), "appid", "hash2", "fp2", ExpectPathExists());
cache->GetByHash("hash", ExpectError(UnpackerError::kCrxCacheFileNotCached));
cache->GetByHash("hash2", ExpectPathExists().Then(Quit()));
RunLoop();
}
TEST_F(CrxCacheTest, PutAlreadyCached) {
scoped_refptr<CrxCache> cache =
base::MakeRefCounted<CrxCache>(TempPath("cache_dir"));
cache->Put(MakeFile(), "appid", "hash", "fp", ExpectPathExists());
cache->GetByHash(
"hash",
base::BindLambdaForTesting(
[&](base::expected<base::FilePath, UnpackerError> result) {
if (!result.has_value()) {
Quit().Run();
}
ASSERT_TRUE(result.has_value()) << static_cast<int>(result.error());
cache->Put(
result.value(), "appid", "hash", "fp",
base::BindLambdaForTesting(
[&](base::expected<base::FilePath, UnpackerError> result2) {
if (!result2.has_value()) {
Quit().Run();
}
ASSERT_TRUE(result2.has_value())
<< static_cast<int>(result2.error());
cache->GetByHash("hash", ExpectPathExists().Then(Quit()));
}));
}));
RunLoop();
}
TEST_F(CrxCacheTest, CacheNotProvided) {
scoped_refptr<CrxCache> cache = base::MakeRefCounted<CrxCache>(std::nullopt);
cache->Put(base::FilePath(FILE_PATH_LITERAL("crxcache_test_file")), "appid",
"hash", "fp", ExpectError(UnpackerError::kCrxCacheNotProvided));
cache->GetByFp("fp", ExpectError(UnpackerError::kCrxCacheNotProvided));
cache->GetByHash("hash", ExpectError(UnpackerError::kCrxCacheNotProvided));
cache->RemoveAll("appid", base::DoNothing());
cache->ListHashesByAppId(ExpectHashes({}).Then(Quit()));
RunLoop();
}
TEST_F(CrxCacheTest, ListHashesByAppId) {
scoped_refptr<CrxCache> cache =
base::MakeRefCounted<CrxCache>(TempPath("cache_dir"));
cache->ListHashesByAppId(ExpectHashes({}));
cache->Put(MakeFile(), "appid", "hash", "fp", ExpectPathExists());
cache->ListHashesByAppId(ExpectHashes({{"appid", "hash"}}));
cache->Put(MakeFile(), "appid2", "hash2", "fp2", ExpectPathExists());
cache->ListHashesByAppId(
ExpectHashes({{"appid", "hash"}, {"appid2", "hash2"}}).Then(Quit()));
RunLoop();
cache->Put(MakeFile(), "appid", "hash3", "fp3", ExpectPathExists());
cache->ListHashesByAppId(
ExpectHashes({{"appid", "hash3"}, {"appid2", "hash2"}}).Then(Quit()));
RunLoop();
}
TEST_F(CrxCacheTest, RemoveAll) {
scoped_refptr<CrxCache> cache =
base::MakeRefCounted<CrxCache>(TempPath("cache_dir"));
cache->Put(MakeFile(), "appid", "hash", "fp", ExpectPathExists());
cache->Put(MakeFile(), "appid2", "hash2", "fp2", ExpectPathExists());
cache->ListHashesByAppId(
ExpectHashes({{"appid", "hash"}, {"appid2", "hash2"}}).Then(Quit()));
RunLoop();
cache->RemoveAll("appid", base::DoNothing());
cache->ListHashesByAppId(ExpectHashes({{"appid2", "hash2"}}).Then(Quit()));
RunLoop();
cache->RemoveAll("appid2", base::DoNothing());
cache->ListHashesByAppId(ExpectHashes({}).Then(Quit()));
RunLoop();
}
} // namespace update_client