blob: fdca8c73872592efea18f3799dfe7d9634694225 [file] [log] [blame]
// Copyright 2013 The Chromium 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 "components/drive/resource_metadata_storage.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_split.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/drive/chromeos/drive_test_util.h"
#include "components/drive/drive.pb.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace drive {
namespace internal {
class ResourceMetadataStorageTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
}
// Overwrites |storage_|'s version.
void SetDBVersion(int version) {
ResourceMetadataHeader header;
ASSERT_EQ(FILE_ERROR_OK, storage_->GetHeader(&header));
header.set_version(version);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutHeader(header));
}
bool CheckValidity() {
return storage_->CheckValidity();
}
leveldb::DB* resource_map() { return storage_->resource_map_.get(); }
// Puts a child entry.
void PutChild(const std::string& parent_id,
const std::string& child_base_name,
const std::string& child_id) {
storage_->resource_map_->Put(
leveldb::WriteOptions(),
ResourceMetadataStorage::GetChildEntryKey(parent_id, child_base_name),
child_id);
}
// Removes a child entry.
void RemoveChild(const std::string& parent_id,
const std::string& child_base_name) {
storage_->resource_map_->Delete(
leveldb::WriteOptions(),
ResourceMetadataStorage::GetChildEntryKey(parent_id, child_base_name));
}
content::TestBrowserThreadBundle thread_bundle_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
storage_;
};
TEST_F(ResourceMetadataStorageTest, LargestChangestamp) {
const int64_t kLargestChangestamp = 1234567890;
EXPECT_EQ(FILE_ERROR_OK,
storage_->SetLargestChangestamp(kLargestChangestamp));
int64_t value = 0;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetLargestChangestamp(&value));
EXPECT_EQ(kLargestChangestamp, value);
}
TEST_F(ResourceMetadataStorageTest, PutEntry) {
const std::string key1 = "abcdefg";
const std::string key2 = "abcd";
const std::string key3 = "efgh";
const std::string name2 = "ABCD";
const std::string name3 = "EFGH";
// key1 not found.
ResourceEntry result;
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key1, &result));
// Put entry1.
ResourceEntry entry1;
entry1.set_local_id(key1);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry1));
// key1 found.
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(key1, &result));
// key2 not found.
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key2, &result));
// Put entry2 as a child of entry1.
ResourceEntry entry2;
entry2.set_local_id(key2);
entry2.set_parent_local_id(key1);
entry2.set_base_name(name2);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry2));
// key2 found.
std::string child_id;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(key2, &result));
EXPECT_EQ(FILE_ERROR_OK, storage_->GetChild(key1, name2, &child_id));
EXPECT_EQ(key2, child_id);
// Put entry3 as a child of entry2.
ResourceEntry entry3;
entry3.set_local_id(key3);
entry3.set_parent_local_id(key2);
entry3.set_base_name(name3);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry3));
// key3 found.
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(key3, &result));
EXPECT_EQ(FILE_ERROR_OK, storage_->GetChild(key2, name3, &child_id));
EXPECT_EQ(key3, child_id);
// Change entry3's parent to entry1.
entry3.set_parent_local_id(key1);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry3));
// entry3 is a child of entry1 now.
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetChild(key2, name3, &child_id));
EXPECT_EQ(FILE_ERROR_OK, storage_->GetChild(key1, name3, &child_id));
EXPECT_EQ(key3, child_id);
// Remove entries.
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(key3));
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key3, &result));
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(key2));
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key2, &result));
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(key1));
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key1, &result));
}
TEST_F(ResourceMetadataStorageTest, Iterator) {
// Prepare data.
std::vector<std::string> keys;
keys.push_back("entry1");
keys.push_back("entry2");
keys.push_back("entry3");
keys.push_back("entry4");
for (size_t i = 0; i < keys.size(); ++i) {
ResourceEntry entry;
entry.set_local_id(keys[i]);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
}
// Iterate and check the result.
std::map<std::string, ResourceEntry> found_entries;
std::unique_ptr<ResourceMetadataStorage::Iterator> it =
storage_->GetIterator();
ASSERT_TRUE(it);
for (; !it->IsAtEnd(); it->Advance()) {
const ResourceEntry& entry = it->GetValue();
found_entries[it->GetID()] = entry;
}
EXPECT_FALSE(it->HasError());
EXPECT_EQ(keys.size(), found_entries.size());
for (size_t i = 0; i < keys.size(); ++i)
EXPECT_EQ(1U, found_entries.count(keys[i]));
}
TEST_F(ResourceMetadataStorageTest, GetIdByResourceId) {
const std::string local_id = "local_id";
const std::string resource_id = "resource_id";
// Resource ID to local ID mapping is not stored yet.
std::string id;
EXPECT_EQ(FILE_ERROR_NOT_FOUND,
storage_->GetIdByResourceId(resource_id, &id));
// Put an entry with the resource ID.
ResourceEntry entry;
entry.set_local_id(local_id);
entry.set_resource_id(resource_id);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
// Can get local ID by resource ID.
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId(resource_id, &id));
EXPECT_EQ(local_id, id);
// Resource ID to local ID mapping is removed.
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(local_id));
EXPECT_EQ(FILE_ERROR_NOT_FOUND,
storage_->GetIdByResourceId(resource_id, &id));
}
TEST_F(ResourceMetadataStorageTest, GetChildren) {
const std::string parents_id[] = { "mercury", "venus", "mars", "jupiter",
"saturn" };
std::vector<base::StringPairs> children_name_id(arraysize(parents_id));
// Skip children_name_id[0/1] here because Mercury and Venus have no moon.
children_name_id[2].push_back(std::make_pair("phobos", "mars_i"));
children_name_id[2].push_back(std::make_pair("deimos", "mars_ii"));
children_name_id[3].push_back(std::make_pair("io", "jupiter_i"));
children_name_id[3].push_back(std::make_pair("europa", "jupiter_ii"));
children_name_id[3].push_back(std::make_pair("ganymede", "jupiter_iii"));
children_name_id[3].push_back(std::make_pair("calisto", "jupiter_iv"));
children_name_id[4].push_back(std::make_pair("mimas", "saturn_i"));
children_name_id[4].push_back(std::make_pair("enceladus", "saturn_ii"));
children_name_id[4].push_back(std::make_pair("tethys", "saturn_iii"));
children_name_id[4].push_back(std::make_pair("dione", "saturn_iv"));
children_name_id[4].push_back(std::make_pair("rhea", "saturn_v"));
children_name_id[4].push_back(std::make_pair("titan", "saturn_vi"));
children_name_id[4].push_back(std::make_pair("iapetus", "saturn_vii"));
// Put parents.
for (size_t i = 0; i < arraysize(parents_id); ++i) {
ResourceEntry entry;
entry.set_local_id(parents_id[i]);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
}
// Put children.
for (size_t i = 0; i < children_name_id.size(); ++i) {
for (size_t j = 0; j < children_name_id[i].size(); ++j) {
ResourceEntry entry;
entry.set_local_id(children_name_id[i][j].second);
entry.set_parent_local_id(parents_id[i]);
entry.set_base_name(children_name_id[i][j].first);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
}
}
// Try to get children.
for (size_t i = 0; i < children_name_id.size(); ++i) {
std::vector<std::string> children;
storage_->GetChildren(parents_id[i], &children);
EXPECT_EQ(children_name_id[i].size(), children.size());
for (size_t j = 0; j < children_name_id[i].size(); ++j) {
EXPECT_EQ(1, std::count(children.begin(),
children.end(),
children_name_id[i][j].second));
}
}
}
TEST_F(ResourceMetadataStorageTest, OpenExistingDB) {
const std::string parent_id1 = "abcdefg";
const std::string child_name1 = "WXYZABC";
const std::string child_id1 = "qwerty";
ResourceEntry entry1;
entry1.set_local_id(parent_id1);
ResourceEntry entry2;
entry2.set_local_id(child_id1);
entry2.set_parent_local_id(parent_id1);
entry2.set_base_name(child_name1);
// Put some data.
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry1));
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry2));
// Close DB and reopen.
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// Can read data.
ResourceEntry result;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(parent_id1, &result));
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(child_id1, &result));
EXPECT_EQ(parent_id1, result.parent_local_id());
EXPECT_EQ(child_name1, result.base_name());
std::string child_id;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetChild(parent_id1, child_name1, &child_id));
EXPECT_EQ(child_id1, child_id);
}
TEST_F(ResourceMetadataStorageTest, IncompatibleDB_M29) {
const int64_t kLargestChangestamp = 1234567890;
const std::string title = "title";
// Construct M29 version DB.
SetDBVersion(6);
EXPECT_EQ(FILE_ERROR_OK,
storage_->SetLargestChangestamp(kLargestChangestamp));
leveldb::WriteBatch batch;
// Put a file entry and its cache entry.
ResourceEntry entry;
std::string serialized_entry;
entry.set_title(title);
entry.set_resource_id("file:abcd");
EXPECT_TRUE(entry.SerializeToString(&serialized_entry));
batch.Put("file:abcd", serialized_entry);
FileCacheEntry cache_entry;
EXPECT_TRUE(cache_entry.SerializeToString(&serialized_entry));
batch.Put(std::string("file:abcd") + '\0' + "CACHE", serialized_entry);
EXPECT_TRUE(resource_map()->Write(leveldb::WriteOptions(), &batch).ok());
// Upgrade and reopen.
storage_.reset();
EXPECT_TRUE(ResourceMetadataStorage::UpgradeOldDB(temp_dir_.GetPath()));
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// Resource-ID-to-local-ID mapping is added.
std::string id;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetIdByResourceId("abcd", &id)); // "file:" is dropped.
// Data is erased, except cache entries.
int64_t largest_changestamp = 0;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetLargestChangestamp(&largest_changestamp));
EXPECT_EQ(0, largest_changestamp);
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(id, &entry));
EXPECT_TRUE(entry.title().empty());
EXPECT_TRUE(entry.file_specific_info().has_cache_state());
}
TEST_F(ResourceMetadataStorageTest, IncompatibleDB_M32) {
const int64_t kLargestChangestamp = 1234567890;
const std::string title = "title";
const std::string resource_id = "abcd";
const std::string local_id = "local-abcd";
// Construct M32 version DB.
SetDBVersion(11);
EXPECT_EQ(FILE_ERROR_OK,
storage_->SetLargestChangestamp(kLargestChangestamp));
leveldb::WriteBatch batch;
// Put a file entry and its cache and id entry.
ResourceEntry entry;
std::string serialized_entry;
entry.set_title(title);
entry.set_local_id(local_id);
entry.set_resource_id(resource_id);
EXPECT_TRUE(entry.SerializeToString(&serialized_entry));
batch.Put(local_id, serialized_entry);
FileCacheEntry cache_entry;
EXPECT_TRUE(cache_entry.SerializeToString(&serialized_entry));
batch.Put(local_id + '\0' + "CACHE", serialized_entry);
batch.Put('\0' + std::string("ID") + '\0' + resource_id, local_id);
EXPECT_TRUE(resource_map()->Write(leveldb::WriteOptions(), &batch).ok());
// Upgrade and reopen.
storage_.reset();
EXPECT_TRUE(ResourceMetadataStorage::UpgradeOldDB(temp_dir_.GetPath()));
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// Data is erased, except cache and id mapping entries.
std::string id;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId(resource_id, &id));
EXPECT_EQ(local_id, id);
int64_t largest_changestamp = 0;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetLargestChangestamp(&largest_changestamp));
EXPECT_EQ(0, largest_changestamp);
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(id, &entry));
EXPECT_TRUE(entry.title().empty());
EXPECT_TRUE(entry.file_specific_info().has_cache_state());
}
TEST_F(ResourceMetadataStorageTest, IncompatibleDB_M33) {
const int64_t kLargestChangestamp = 1234567890;
const std::string title = "title";
const std::string resource_id = "abcd";
const std::string local_id = "local-abcd";
const std::string md5 = "md5";
const std::string resource_id2 = "efgh";
const std::string local_id2 = "local-efgh";
const std::string md5_2 = "md5_2";
// Construct M33 version DB.
SetDBVersion(12);
EXPECT_EQ(FILE_ERROR_OK,
storage_->SetLargestChangestamp(kLargestChangestamp));
leveldb::WriteBatch batch;
// Put a file entry and its cache and id entry.
ResourceEntry entry;
std::string serialized_entry;
entry.set_title(title);
entry.set_local_id(local_id);
entry.set_resource_id(resource_id);
EXPECT_TRUE(entry.SerializeToString(&serialized_entry));
batch.Put(local_id, serialized_entry);
FileCacheEntry cache_entry;
cache_entry.set_md5(md5);
EXPECT_TRUE(cache_entry.SerializeToString(&serialized_entry));
batch.Put(local_id + '\0' + "CACHE", serialized_entry);
batch.Put('\0' + std::string("ID") + '\0' + resource_id, local_id);
// Put another cache entry which is not accompanied by a ResourceEntry.
cache_entry.set_md5(md5_2);
EXPECT_TRUE(cache_entry.SerializeToString(&serialized_entry));
batch.Put(local_id2 + '\0' + "CACHE", serialized_entry);
batch.Put('\0' + std::string("ID") + '\0' + resource_id2, local_id2);
EXPECT_TRUE(resource_map()->Write(leveldb::WriteOptions(), &batch).ok());
// Upgrade and reopen.
storage_.reset();
EXPECT_TRUE(ResourceMetadataStorage::UpgradeOldDB(temp_dir_.GetPath()));
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// largest_changestamp is cleared.
int64_t largest_changestamp = 0;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetLargestChangestamp(&largest_changestamp));
EXPECT_EQ(0, largest_changestamp);
// No other data is lost.
std::string id;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId(resource_id, &id));
EXPECT_EQ(local_id, id);
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(id, &entry));
EXPECT_EQ(title, entry.title());
EXPECT_EQ(md5, entry.file_specific_info().cache_state().md5());
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId(resource_id2, &id));
EXPECT_EQ(local_id2, id);
EXPECT_EQ(FILE_ERROR_OK, storage_->GetEntry(id, &entry));
EXPECT_EQ(md5_2, entry.file_specific_info().cache_state().md5());
}
TEST_F(ResourceMetadataStorageTest, IncompatibleDB_Unknown) {
const int64_t kLargestChangestamp = 1234567890;
const std::string key1 = "abcd";
// Put some data.
EXPECT_EQ(FILE_ERROR_OK,
storage_->SetLargestChangestamp(kLargestChangestamp));
ResourceEntry entry;
entry.set_local_id(key1);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
// Set newer version, upgrade and reopen DB.
SetDBVersion(ResourceMetadataStorage::kDBVersion + 1);
storage_.reset();
EXPECT_FALSE(ResourceMetadataStorage::UpgradeOldDB(temp_dir_.GetPath()));
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// Data is erased because of the incompatible version.
int64_t largest_changestamp = 0;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetLargestChangestamp(&largest_changestamp));
EXPECT_EQ(0, largest_changestamp);
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key1, &entry));
}
TEST_F(ResourceMetadataStorageTest, IncompatibleDB_M37) {
const int64_t kLargestChangestamp = 1234567890;
// Construct v13 DB. It was used from M37 to M62.
SetDBVersion(13);
EXPECT_EQ(FILE_ERROR_OK,
storage_->SetLargestChangestamp(kLargestChangestamp));
leveldb::WriteBatch batch;
// Put an ID entry with a corresponding ResourceEntry.
ResourceEntry entry;
entry.set_local_id("id1");
entry.set_resource_id("resource_id1");
std::string serialized_entry;
EXPECT_TRUE(entry.SerializeToString(&serialized_entry));
batch.Put("id1", serialized_entry);
batch.Put('\0' + std::string("ID") + '\0' + "resource_id1", "id1");
// Put an ID entry without any corresponding entries.
batch.Put('\0' + std::string("ID") + '\0' + "resource_id2", "id3");
EXPECT_TRUE(resource_map()->Write(leveldb::WriteOptions(), &batch).ok());
// Upgrade and reopen.
storage_.reset();
EXPECT_TRUE(ResourceMetadataStorage::UpgradeOldDB(temp_dir_.GetPath()));
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// Only the unused entry is deleted.
std::string id;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId("resource_id1", &id));
EXPECT_EQ("id1", id);
EXPECT_EQ(FILE_ERROR_NOT_FOUND,
storage_->GetIdByResourceId("resource_id2", &id));
// largest_changestamp is cleared.
int64_t largest_changestamp = 0;
EXPECT_EQ(FILE_ERROR_OK,
storage_->GetLargestChangestamp(&largest_changestamp));
EXPECT_EQ(0, largest_changestamp);
}
TEST_F(ResourceMetadataStorageTest, WrongPath) {
// Create a file.
base::FilePath path;
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &path));
storage_.reset(new ResourceMetadataStorage(
path, base::ThreadTaskRunnerHandle::Get().get()));
// Cannot initialize DB beacause the path does not point a directory.
ASSERT_FALSE(storage_->Initialize());
}
TEST_F(ResourceMetadataStorageTest, RecoverCacheEntriesFromTrashedResourceMap) {
// Put entry with id_foo.
ResourceEntry entry;
entry.set_local_id("id_foo");
entry.set_base_name("foo");
entry.set_title("foo");
entry.mutable_file_specific_info()->mutable_cache_state()->set_md5("md5_foo");
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
// Put entry with id_bar as a id_foo's child.
entry.set_local_id("id_bar");
entry.set_parent_local_id("id_foo");
entry.set_base_name("bar");
entry.set_title("bar");
entry.mutable_file_specific_info()->mutable_cache_state()->set_md5("md5_bar");
entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
// Remove parent-child relationship to make the DB invalid.
RemoveChild("id_foo", "bar");
EXPECT_FALSE(CheckValidity());
// Reopen. This should result in trashing the DB.
storage_.reset(new ResourceMetadataStorage(
temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
ASSERT_TRUE(storage_->Initialize());
// Recover cache entries from the trashed DB.
ResourceMetadataStorage::RecoveredCacheInfoMap recovered_cache_info;
storage_->RecoverCacheInfoFromTrashedResourceMap(&recovered_cache_info);
EXPECT_EQ(2U, recovered_cache_info.size());
EXPECT_FALSE(recovered_cache_info["id_foo"].is_dirty);
EXPECT_EQ("md5_foo", recovered_cache_info["id_foo"].md5);
EXPECT_EQ("foo", recovered_cache_info["id_foo"].title);
EXPECT_TRUE(recovered_cache_info["id_bar"].is_dirty);
EXPECT_EQ("md5_bar", recovered_cache_info["id_bar"].md5);
EXPECT_EQ("bar", recovered_cache_info["id_bar"].title);
}
TEST_F(ResourceMetadataStorageTest, CheckValidity) {
const std::string key1 = "foo";
const std::string name1 = "hoge";
const std::string key2 = "bar";
const std::string name2 = "fuga";
const std::string key3 = "boo";
const std::string name3 = "piyo";
// Empty storage is valid.
EXPECT_TRUE(CheckValidity());
// Put entry with key1.
ResourceEntry entry;
entry.set_local_id(key1);
entry.set_base_name(name1);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
EXPECT_TRUE(CheckValidity());
// Put entry with key2 under key1.
entry.set_local_id(key2);
entry.set_parent_local_id(key1);
entry.set_base_name(name2);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
EXPECT_TRUE(CheckValidity());
RemoveChild(key1, name2);
EXPECT_FALSE(CheckValidity()); // Missing parent-child relationship.
// Add back parent-child relationship between key1 and key2.
PutChild(key1, name2, key2);
EXPECT_TRUE(CheckValidity());
// Add parent-child relationship between key2 and key3.
PutChild(key2, name3, key3);
EXPECT_FALSE(CheckValidity()); // key3 is not stored in the storage.
// Put entry with key3 under key2.
entry.set_local_id(key3);
entry.set_parent_local_id(key2);
entry.set_base_name(name3);
EXPECT_EQ(FILE_ERROR_OK, storage_->PutEntry(entry));
EXPECT_TRUE(CheckValidity());
// Parent-child relationship with wrong name.
RemoveChild(key2, name3);
EXPECT_FALSE(CheckValidity());
PutChild(key2, name2, key3);
EXPECT_FALSE(CheckValidity());
// Fix up the relationship between key2 and key3.
RemoveChild(key2, name2);
EXPECT_FALSE(CheckValidity());
PutChild(key2, name3, key3);
EXPECT_TRUE(CheckValidity());
// Remove key2.
RemoveChild(key1, name2);
EXPECT_FALSE(CheckValidity());
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(key2));
EXPECT_FALSE(CheckValidity());
// Remove key3.
RemoveChild(key2, name3);
EXPECT_FALSE(CheckValidity());
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(key3));
EXPECT_TRUE(CheckValidity());
// Remove key1.
EXPECT_EQ(FILE_ERROR_OK, storage_->RemoveEntry(key1));
EXPECT_TRUE(CheckValidity());
}
} // namespace internal
} // namespace drive