blob: 9e7ea815a0c0c53ba102bf140401f4135dc193e7 [file] [log] [blame]
// Copyright 2019 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/sys/appmgr/storage_watchdog.h"
#include <lib/async/cpp/task.h>
#include <lib/fdio/namespace.h>
#include <lib/gtest/real_loop_fixture.h>
#include <lib/memfs/memfs.h>
#include <lib/sync/completion.h>
#include <algorithm>
#include <string>
#include <vector>
#include <src/lib/files/directory.h>
#include <src/lib/files/file.h>
#include <src/lib/files/path.h>
#include "gtest/gtest.h"
namespace {
namespace fio = fuchsia_io;
const char* kTmpData = "abcdefghijklmnopqrstuvwxyz1234567890";
class StorageWatchdogTest : public ::testing::Test {
public:
StorageWatchdogTest() : loop_(async::Loop(&kAsyncLoopConfigAttachToCurrentThread)) {}
void SetUp() override {
testing::Test::SetUp();
ASSERT_EQ(ZX_OK,
memfs_create_filesystem(loop_.dispatcher(), &memfs_handle_, &memfs_root_handle_));
ASSERT_EQ(ZX_OK, fdio_ns_get_installed(&ns_));
ASSERT_EQ(ZX_OK, fdio_ns_bind(ns_, "/hippo_storage", memfs_root_handle_));
ASSERT_EQ(ZX_OK, loop_.StartThread());
}
// Set up the async loop, create memfs, install memfs at /hippo_storage
void TearDown() override {
// Unbind memfs from our namespace, free memfs
ASSERT_EQ(ZX_OK, fdio_ns_unbind(ns_, "/hippo_storage"));
sync_completion_t memfs_freed_signal;
memfs_free_filesystem(memfs_handle_, &memfs_freed_signal);
ASSERT_EQ(ZX_OK, sync_completion_wait(&memfs_freed_signal, ZX_SEC(5)));
}
private:
async::Loop loop_;
memfs_filesystem_t* memfs_handle_;
zx_handle_t memfs_root_handle_;
fdio_ns_t* ns_;
};
class TestStorageWatchdog : public StorageWatchdog {
public:
TestStorageWatchdog(std::string path_to_watch, std::string path_to_clean)
: StorageWatchdog(path_to_watch, path_to_clean) {}
zx_status_t GetFilesystemInfo(zx_handle_t directory, fio::wire::FilesystemInfo* out_info) {
if (directory == ZX_HANDLE_INVALID) {
return ZX_ERR_BAD_HANDLE;
}
*out_info = info;
return ZX_OK;
}
fio::wire::FilesystemInfo info = {};
};
TEST_F(StorageWatchdogTest, Basic) {
const std::string kRootPath = "/hippo_storage/cache";
const std::string kRealmPath = files::JoinPath(kRootPath, "r/sys");
const std::string kNestedRealmPath = files::JoinPath(kRootPath, "r/sys/r/test");
const std::string kV1Path = files::JoinPath(kRealmPath, "fuchsia.com:cobalt:0#meta:cobalt.cmx");
const std::string kV1NestedPath =
files::JoinPath(kNestedRealmPath, "fuchsia.com:cobalt-unittest:0#meta:cobalt-unittest.cmx");
const std::string kV2IdPath = files::JoinPath(
kRootPath, "e5ef2bbe9dd2b7cee87beac5e06cece13fe6f9c154b1f00abec155e6c6c9fa62");
const std::string kV2MonikerBasePath = files::JoinPath(kRootPath, "network:0");
const std::string kV2MonikerPath = files::JoinPath(kV2MonikerBasePath, "data");
const std::string kV2NestedMonikerBasePath =
files::JoinPath(kRootPath, "network:0/children/netstack:0");
const std::string kV2NestedMonikerPath = files::JoinPath(kV2NestedMonikerBasePath, "data");
TestStorageWatchdog watchdog = TestStorageWatchdog("/hippo_storage", "/hippo_storage/cache");
watchdog.info.used_bytes = 0;
watchdog.info.total_bytes = 20 * 1024;
auto usage = watchdog.GetStorageUsage();
EXPECT_LE(usage.percent(), StorageWatchdog::kCachePurgeThresholdPct);
for (size_t i = 0; i < 10; ++i) {
auto filename = std::to_string(i);
for (const std::string& path :
{kV1Path, kV1NestedPath, kV2IdPath, kV2MonikerPath, kV2NestedMonikerPath}) {
ASSERT_TRUE(files::CreateDirectory(path));
ASSERT_TRUE(files::WriteFile(files::JoinPath(path, filename), kTmpData, strlen(kTmpData)));
}
}
watchdog.info.used_bytes = watchdog.info.total_bytes - 128;
// Confirm that storage pressure is high, clear the cache, check that things
// were actually deleted (but the directories themselves were preserved).
usage = watchdog.GetStorageUsage();
EXPECT_GT(usage.percent(), StorageWatchdog::kCachePurgeThresholdPct);
watchdog.PurgeCache();
// For each case, check:
// - The contents of the storage dir are cleared.
// - The storage dir itself is not deleted.
// V1
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV1Path, &files));
EXPECT_EQ(1ul, files.size());
EXPECT_EQ(files[0], ".");
}
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kRealmPath, &files));
EXPECT_EQ(3ul, files.size());
std::sort(files.begin(), files.end());
EXPECT_EQ(files[0], ".");
EXPECT_EQ(files[1], "fuchsia.com:cobalt:0#meta:cobalt.cmx");
EXPECT_EQ(files[2], "r");
}
// V1 nested
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV1NestedPath, &files));
EXPECT_EQ(1ul, files.size());
EXPECT_EQ(files[0], ".");
}
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kNestedRealmPath, &files));
EXPECT_EQ(2ul, files.size());
std::sort(files.begin(), files.end());
EXPECT_EQ(files[0], ".");
EXPECT_EQ(files[1], "fuchsia.com:cobalt-unittest:0#meta:cobalt-unittest.cmx");
}
// V2 instance id
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV2IdPath, &files));
EXPECT_EQ(1ul, files.size());
EXPECT_EQ(files[0], ".");
}
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kRootPath, &files));
EXPECT_EQ(4ul, files.size());
std::sort(files.begin(), files.end());
EXPECT_EQ(files[0], ".");
EXPECT_EQ(files[1], "e5ef2bbe9dd2b7cee87beac5e06cece13fe6f9c154b1f00abec155e6c6c9fa62");
EXPECT_EQ(files[2], "network:0");
EXPECT_EQ(files[3], "r");
}
// V2 moniker
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV2MonikerPath, &files));
EXPECT_EQ(1ul, files.size());
EXPECT_EQ(files[0], ".");
}
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV2MonikerBasePath, &files));
EXPECT_EQ(3ul, files.size());
std::sort(files.begin(), files.end());
EXPECT_EQ(files[0], ".");
EXPECT_EQ(files[1], "children");
EXPECT_EQ(files[2], "data");
}
// V2 nested moniker
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV2NestedMonikerPath, &files));
EXPECT_EQ(1ul, files.size());
EXPECT_EQ(files[0], ".");
}
{
std::vector<std::string> files = {};
EXPECT_TRUE(files::ReadDirContents(kV2NestedMonikerBasePath, &files));
EXPECT_EQ(2ul, files.size());
std::sort(files.begin(), files.end());
EXPECT_EQ(files[0], ".");
EXPECT_EQ(files[1], "data");
}
}
} // namespace