blob: 2e62fcae6b744c27172fa24bb8f13d95c02ce8d1 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ledger/bin/app/disk_cleanup_manager_impl.h"
#include <memory>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/ledger/bin/app/constants.h"
#include "src/ledger/bin/app/db_view_factory.h"
#include "src/ledger/bin/app/serialization.h"
#include "src/ledger/bin/platform/scoped_tmp_location.h"
#include "src/ledger/bin/storage/fake/fake_db_factory.h"
#include "src/ledger/bin/testing/test_with_environment.h"
#include "src/ledger/lib/convert/convert.h"
#include "src/ledger/lib/loop_fixture/test_loop_fixture.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
namespace ledger {
namespace {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
class FakeDelegate : public PageEvictionManager::Delegate {
public:
void PageIsClosedAndSynced(absl::string_view /*ledger_name*/, storage::PageIdView /*page_id*/,
fit::function<void(Status, PagePredicateResult)> callback) override {
callback(Status::OK, PagePredicateResult::YES);
}
void PageIsClosedOfflineAndEmpty(
absl::string_view ledger_name, storage::PageIdView page_id,
fit::function<void(Status, PagePredicateResult)> callback) override {
callback(Status::OK, closed_offline_empty);
}
void DeletePageStorage(absl::string_view /*ledger_name*/, storage::PageIdView page_id,
fit::function<void(Status)> callback) override {
deleted_pages.push_back(convert::ToString(page_id));
callback(Status::OK);
}
std::vector<storage::PageId> deleted_pages;
PagePredicateResult closed_offline_empty = PagePredicateResult::YES;
};
class DiskCleanupManagerTest : public TestWithEnvironment {
public:
DiskCleanupManagerTest()
: tmp_location_(environment_.file_system()->CreateScopedTmpLocation()),
db_factory_(environment_.file_system(), environment_.dispatcher()) {}
DiskCleanupManagerTest(const DiskCleanupManagerTest&) = delete;
DiskCleanupManagerTest& operator=(const DiskCleanupManagerTest&) = delete;
// ledger::TestLoopFixture:
void SetUp() override {
ResetPageUsageDb();
disk_cleanup_manager_ = std::make_unique<DiskCleanupManagerImpl>(&environment_, db_.get());
disk_cleanup_manager_->SetPageEvictionDelegate(&delegate_);
}
void ResetPageUsageDb() {
EXPECT_TRUE(RunInCoroutine([this](coroutine::CoroutineHandler* handler) {
Status status;
std::unique_ptr<storage::Db> leveldb;
if (coroutine::SyncCall(
handler,
[this](fit::function<void(Status, std::unique_ptr<storage::Db>)> callback) mutable {
db_factory_.GetOrCreateDb(tmp_location_->path(),
storage::DbFactory::OnDbNotFound::CREATE,
std::move(callback));
},
&status, &leveldb) == coroutine::ContinuationStatus::INTERRUPTED) {
// This should not be reached.
FAIL();
return;
}
dbview_factory_ = std::make_unique<DbViewFactory>(std::move(leveldb));
db_ = std::make_unique<PageUsageDb>(
&environment_, dbview_factory_->CreateDbView(RepositoryRowPrefix::PAGE_USAGE_DB));
}));
}
private:
std::unique_ptr<ScopedTmpLocation> tmp_location_;
storage::fake::FakeDbFactory db_factory_;
std::unique_ptr<DbViewFactory> dbview_factory_;
std::unique_ptr<PageUsageDb> db_;
protected:
FakeDelegate delegate_;
std::unique_ptr<DiskCleanupManagerImpl> disk_cleanup_manager_;
};
TEST_F(DiskCleanupManagerTest, DontEvictNonEmptyPagesOnPageUnused) {
std::string ledger_name = "ledger";
storage::PageId page = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '1');
// The page cannot be evicted if its not empty and offline.
disk_cleanup_manager_->OnExternallyUsed(ledger_name, page);
delegate_.closed_offline_empty = PagePredicateResult::NO;
disk_cleanup_manager_->OnExternallyUnused(ledger_name, page);
RunLoopUntilIdle();
EXPECT_THAT(delegate_.deleted_pages, IsEmpty());
}
TEST_F(DiskCleanupManagerTest, DontEvictUnknownEmptyPagesOnPageUnused) {
std::string ledger_name = "ledger";
storage::PageId page = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '1');
// The page cannot be evicted if we can't determine whether it is empty and
// offline.
disk_cleanup_manager_->OnExternallyUsed(ledger_name, page);
delegate_.closed_offline_empty = PagePredicateResult::PAGE_OPENED;
disk_cleanup_manager_->OnExternallyUnused(ledger_name, page);
RunLoopUntilIdle();
EXPECT_THAT(delegate_.deleted_pages, IsEmpty());
}
TEST_F(DiskCleanupManagerTest, EvictEmptyOfflinePagesOnPageUnused) {
std::string ledger_name = "ledger";
storage::PageId page = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '1');
// The page should be evicted is it is empty and offline.
disk_cleanup_manager_->OnExternallyUsed(ledger_name, page);
delegate_.closed_offline_empty = PagePredicateResult::YES;
disk_cleanup_manager_->OnExternallyUnused(ledger_name, page);
RunLoopUntilIdle();
EXPECT_THAT(delegate_.deleted_pages, ElementsAre(page));
}
TEST_F(DiskCleanupManagerTest, DontEvictPageWhenInternalConnectionsRemain) {
std::string ledger_name = "ledger";
storage::PageId page = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '1');
disk_cleanup_manager_->OnExternallyUsed(ledger_name, page);
disk_cleanup_manager_->OnInternallyUsed(ledger_name, page);
disk_cleanup_manager_->OnInternallyUsed(ledger_name, page);
disk_cleanup_manager_->OnInternallyUnused(ledger_name, page);
delegate_.closed_offline_empty = PagePredicateResult::YES;
disk_cleanup_manager_->OnExternallyUnused(ledger_name, page);
RunLoopUntilIdle();
EXPECT_THAT(delegate_.deleted_pages, IsEmpty());
}
TEST_F(DiskCleanupManagerTest, DontEvictPageWhenExternalConnectionsRemain) {
std::string ledger_name = "ledger";
storage::PageId page = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '1');
disk_cleanup_manager_->OnInternallyUsed(ledger_name, page);
disk_cleanup_manager_->OnExternallyUsed(ledger_name, page);
disk_cleanup_manager_->OnExternallyUsed(ledger_name, page);
disk_cleanup_manager_->OnExternallyUnused(ledger_name, page);
delegate_.closed_offline_empty = PagePredicateResult::YES;
disk_cleanup_manager_->OnInternallyUnused(ledger_name, page);
RunLoopUntilIdle();
EXPECT_THAT(delegate_.deleted_pages, IsEmpty());
}
TEST_F(DiskCleanupManagerTest, DontEvictPageWhenNoExternalConnectionHappened) {
std::string ledger_name = "ledger";
storage::PageId page = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '1');
disk_cleanup_manager_->OnInternallyUsed(ledger_name, page);
delegate_.closed_offline_empty = PagePredicateResult::YES;
disk_cleanup_manager_->OnInternallyUnused(ledger_name, page);
RunLoopUntilIdle();
EXPECT_THAT(delegate_.deleted_pages, IsEmpty());
}
} // namespace
} // namespace ledger