blob: cefe04cb808e30248eddff25c0b892ff7bd66b64 [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/chromeos/gdata/drive_cache_metadata.h"
#include "base/file_util.h"
#include "base/scoped_temp_dir.h"
#include "chrome/browser/chromeos/gdata/drive.pb.h"
#include "chrome/browser/chromeos/gdata/drive_file_system_util.h"
#include "chrome/browser/chromeos/gdata/drive_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gdata {
class DriveCacheMetadataTest : public testing::Test {
public:
DriveCacheMetadataTest() {}
virtual void SetUp() OVERRIDE {
// Create cache directories.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
cache_paths_ = DriveCache::GetCachePaths(temp_dir_.path());
ASSERT_TRUE(DriveCache::CreateCacheDirectories(cache_paths_));
persistent_directory_ = cache_paths_[DriveCache::CACHE_TYPE_PERSISTENT];
tmp_directory_ = cache_paths_[DriveCache::CACHE_TYPE_TMP];
pinned_directory_ = cache_paths_[DriveCache::CACHE_TYPE_PINNED];
outgoing_directory_ = cache_paths_[DriveCache::CACHE_TYPE_OUTGOING];
}
virtual void TearDown() OVERRIDE {
metadata_.reset();
}
// Sets up the DriveCacheMetadata object.
void SetUpCacheMetadata() {
metadata_ = DriveCacheMetadata::CreateDriveCacheMetadata(NULL).Pass();
metadata_->Initialize(cache_paths_);
}
// Sets up the cache directories with various files, including stale
// symbolic links etc. Should be called before SetUpCacheMetadata().
void SetUpCacheWithVariousFiles() {
// Create some files in persistent directory.
//
CreateFile(persistent_directory_.AppendASCII("id_foo.md5foo"));
CreateFile(persistent_directory_.AppendASCII("id_bar.local"));
// "id_baz" is dirty but does not have a symlink in outgoing
// directory. This file should be removed.
CreateFile(persistent_directory_.AppendASCII("id_baz.local"));
// "id_bad" is in persistent directory, but does not have a link in
// pinned directory. This file should be removed.
CreateFile(persistent_directory_.AppendASCII("id_bad.md5bad"));
// "id_symlink" is invalid, as symlink is not allowed here. This should
// be removed.
CreateSymbolicLink(FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull),
persistent_directory_.AppendASCII("id_symlink"));
// Create some files in tmp directory.
//
CreateFile(tmp_directory_.AppendASCII("id_qux.md5qux"));
// "id_quux" is invalid as we shouldn't have a dirty file in "tmp".
CreateFile(tmp_directory_.AppendASCII("id_quux.local"));
// "id_symlink_tmp" is invalid, as symlink is not allowed here. This
// should be removed.
CreateSymbolicLink(FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull),
tmp_directory_.AppendASCII("id_symlink_tmp"));
// Create symbolic links in pinned directory.
//
// "id_foo" is pinned, and present locally.
CreateSymbolicLink(persistent_directory_.AppendASCII("id_foo.md5foo"),
pinned_directory_.AppendASCII("id_foo"));
// "id_corge" is pinned, but not present locally. It's properly pointing
// to /dev/null.
CreateSymbolicLink(FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull),
pinned_directory_.AppendASCII("id_corge"));
// "id_dangling" is pointing to a non-existent file. The symlink should
// be removed.
CreateSymbolicLink(persistent_directory_.AppendASCII("id_dangling.md5foo"),
pinned_directory_.AppendASCII("id_dangling"));
// "id_outside" is pointing to a file outside of persistent
// directory. The symlink should be removed.
CreateSymbolicLink(tmp_directory_.AppendASCII("id_qux.md5qux"),
pinned_directory_.AppendASCII("id_outside"));
// "id_not_symlink" is not a symlink. This should be removed.
CreateFile(pinned_directory_.AppendASCII("id_not_symlink"));
// Create symbolic links in outgoing directory.
//
// "id_bar" is dirty and committed.
CreateSymbolicLink(persistent_directory_.AppendASCII("id_bar.local"),
outgoing_directory_.AppendASCII("id_bar"));
// "id_foo" is not dirty. This symlink should be removed.
CreateSymbolicLink(persistent_directory_.AppendASCII("id_foo.md5foo"),
outgoing_directory_.AppendASCII("id_foo"));
}
// Create a file at |file_path|.
void CreateFile(const FilePath& file_path) {
const std::string kFoo = "foo";
ASSERT_TRUE(file_util::WriteFile(file_path, kFoo.data(), kFoo.size()))
<< ": " << file_path.value();
}
// Create an symlink to |target| at |symlink|.
void CreateSymbolicLink(const FilePath& target, const FilePath& symlink) {
ASSERT_TRUE(file_util::CreateSymbolicLink(target, symlink))
<< ": " << target.value() << ": " << symlink.value();
}
protected:
// Helper function to insert an item with key |resource_id| into |cache_map|.
// |md5| and |cache_state| are used to create the value CacheEntry.
void InsertIntoMap(DriveCacheMetadata::CacheMap* cache_map,
const std::string& resource_id,
const DriveCacheEntry& cache_entry) {
cache_map->insert(std::make_pair(
resource_id, cache_entry));
}
// Adds all entries in |cache_map| to the metadata storage.
void AddAllMapEntries(const DriveCacheMetadata::CacheMap& cache_map) {
for (DriveCacheMetadata::CacheMap::const_iterator iter = cache_map.begin();
iter != cache_map.end(); ++iter) {
metadata_->AddOrUpdateCacheEntry(iter->first, iter->second);
}
}
ScopedTempDir temp_dir_;
scoped_ptr<DriveCacheMetadata> metadata_;
std::vector<FilePath> cache_paths_;
FilePath persistent_directory_;
FilePath tmp_directory_;
FilePath pinned_directory_;
FilePath outgoing_directory_;
};
// Test all the methods of DriveCacheMetadata except for
// RemoveTemporaryFiles.
TEST_F(DriveCacheMetadataTest, CacheTest) {
SetUpCacheMetadata();
// Save an initial entry.
std::string test_resource_id("test_resource_id");
std::string test_file_md5("test_file_md5");
{
DriveCacheEntry new_cache_entry;
new_cache_entry.set_md5(test_file_md5);
new_cache_entry.set_is_present(true);
new_cache_entry.set_is_persistent(true);
metadata_->AddOrUpdateCacheEntry(test_resource_id, new_cache_entry);
}
// Test that the entry can be retrieved.
DriveCacheEntry cache_entry;
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, test_file_md5, &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
EXPECT_TRUE(cache_entry.is_present());
EXPECT_TRUE(cache_entry.is_persistent());
// Empty md5 should also work.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, std::string(), &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
// resource_id doesn't exist.
EXPECT_FALSE(metadata_->GetCacheEntry(
"not_found_resource_id", std::string(), &cache_entry));
// md5 doesn't match.
EXPECT_FALSE(metadata_->GetCacheEntry(
test_resource_id, "mismatch_md5", &cache_entry));
// Update all attributes.
test_file_md5 = "test_file_md5_2";
{
DriveCacheEntry updated_cache_entry;
updated_cache_entry.set_md5(test_file_md5);
updated_cache_entry.set_is_pinned(true);
metadata_->AddOrUpdateCacheEntry(test_resource_id, updated_cache_entry);
}
// Make sure the values took.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, test_file_md5, &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
EXPECT_TRUE(cache_entry.is_pinned());
// Empty m5 should work.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, std::string(), &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
// Test dirty cache.
test_file_md5 = "test_file_md5_3";
{
DriveCacheEntry new_cache_entry;
new_cache_entry.set_md5(test_file_md5);
new_cache_entry.set_is_dirty(true);
metadata_->AddOrUpdateCacheEntry(test_resource_id, new_cache_entry);
}
// Make sure the values took.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, test_file_md5, &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
EXPECT_TRUE(cache_entry.is_dirty());
// Empty md5 should work.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, std::string(), &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
// Mismatched md5 should also work for dirty entries.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, "mismatch_md5", &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
// Remove the entry.
metadata_->RemoveCacheEntry(test_resource_id);
EXPECT_FALSE(metadata_->GetCacheEntry(
test_resource_id, std::string(), &cache_entry));
// Add another one.
test_resource_id = "test_resource_id_2";
test_file_md5 = "test_file_md5_4";
{
DriveCacheEntry new_cache_entry;
new_cache_entry.set_md5(test_file_md5);
new_cache_entry.set_is_present(true);
metadata_->AddOrUpdateCacheEntry(test_resource_id, new_cache_entry);
}
// Make sure the values took.
ASSERT_TRUE(metadata_->GetCacheEntry(
test_resource_id, test_file_md5, &cache_entry));
EXPECT_EQ(test_file_md5, cache_entry.md5());
EXPECT_TRUE(cache_entry.is_present());
}
TEST_F(DriveCacheMetadataTest, Initialization) {
using file_util::PathExists;
using file_util::IsLink;
SetUpCacheWithVariousFiles();
// Some files are removed during cache initialization. Make sure these
// exist beforehand.
EXPECT_TRUE(PathExists(persistent_directory_.AppendASCII("id_baz.local")));
EXPECT_TRUE(PathExists(persistent_directory_.AppendASCII("id_bad.md5bad")));
EXPECT_TRUE(PathExists(tmp_directory_.AppendASCII("id_quux.local")));
EXPECT_TRUE(PathExists(pinned_directory_.AppendASCII("id_not_symlink")));
EXPECT_TRUE(IsLink(pinned_directory_.AppendASCII("id_dangling")));
EXPECT_TRUE(IsLink(pinned_directory_.AppendASCII("id_outside")));
EXPECT_TRUE(IsLink(outgoing_directory_.AppendASCII("id_foo")));
EXPECT_TRUE(IsLink(persistent_directory_.AppendASCII("id_symlink")));
EXPECT_TRUE(IsLink(tmp_directory_.AppendASCII("id_symlink_tmp")));
SetUpCacheMetadata();
// Check contents in "persistent" directory.
//
// "id_foo" is present and pinned.
DriveCacheEntry cache_entry;
ASSERT_TRUE(metadata_->GetCacheEntry("id_foo", "md5foo", &cache_entry));
EXPECT_EQ("md5foo", cache_entry.md5());
EXPECT_EQ(DriveCache::CACHE_TYPE_PERSISTENT,
DriveCache::GetSubDirectoryType(cache_entry));
EXPECT_TRUE(test_util::CacheStatesEqual(
test_util::ToCacheEntry(test_util::TEST_CACHE_STATE_PRESENT |
test_util::TEST_CACHE_STATE_PINNED |
test_util::TEST_CACHE_STATE_PERSISTENT),
cache_entry));
EXPECT_TRUE(PathExists(persistent_directory_.AppendASCII("id_foo.md5foo")));
EXPECT_TRUE(PathExists(pinned_directory_.AppendASCII("id_foo")));
// The invalid symlink in "outgoing" should be removed.
EXPECT_FALSE(PathExists(outgoing_directory_.AppendASCII("id_foo")));
// "id_bar" is present and dirty.
ASSERT_TRUE(metadata_->GetCacheEntry("id_bar", "", &cache_entry));
EXPECT_EQ("local", cache_entry.md5());
EXPECT_EQ(DriveCache::CACHE_TYPE_PERSISTENT,
DriveCache::GetSubDirectoryType(cache_entry));
EXPECT_TRUE(test_util::CacheStatesEqual(
test_util::ToCacheEntry(test_util::TEST_CACHE_STATE_PRESENT |
test_util::TEST_CACHE_STATE_DIRTY |
test_util::TEST_CACHE_STATE_PERSISTENT),
cache_entry));
EXPECT_TRUE(PathExists(persistent_directory_.AppendASCII("id_bar.local")));
EXPECT_TRUE(PathExists(outgoing_directory_.AppendASCII("id_bar")));
// "id_baz" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_baz", "", &cache_entry));
EXPECT_FALSE(PathExists(persistent_directory_.AppendASCII("id_baz.local")));
// "id_bad" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_bad", "md5bad", &cache_entry));
EXPECT_FALSE(PathExists(persistent_directory_.AppendASCII("id_bad.md5bad")));
// "id_symlink" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_symlink", "", &cache_entry));
EXPECT_FALSE(PathExists(persistent_directory_.AppendASCII("id_symlink")));
// Check contents in "tmp" directory.
//
// "id_qux" is just present in tmp directory.
ASSERT_TRUE(metadata_->GetCacheEntry("id_qux", "md5qux", &cache_entry));
EXPECT_EQ("md5qux", cache_entry.md5());
EXPECT_EQ(DriveCache::CACHE_TYPE_TMP,
DriveCache::GetSubDirectoryType(cache_entry));
EXPECT_TRUE(test_util::CacheStatesEqual(
test_util::ToCacheEntry(test_util::TEST_CACHE_STATE_PRESENT),
cache_entry));
EXPECT_TRUE(PathExists(tmp_directory_.AppendASCII("id_qux.md5qux")));
// "id_quux" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_quux", "md5qux", &cache_entry));
EXPECT_FALSE(PathExists(pinned_directory_.AppendASCII("id_quux.local")));
// "id_symlink_tmp" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_symlink_tmp", "", &cache_entry));
EXPECT_FALSE(PathExists(pinned_directory_.AppendASCII("id_symlink_tmp")));
// Check contents in "pinned" directory.
//
// "id_corge" is pinned but not present.
ASSERT_TRUE(metadata_->GetCacheEntry("id_corge", "", &cache_entry));
EXPECT_EQ("", cache_entry.md5());
EXPECT_TRUE(test_util::CacheStatesEqual(
test_util::ToCacheEntry(test_util::TEST_CACHE_STATE_PINNED),
cache_entry));
EXPECT_TRUE(IsLink(pinned_directory_.AppendASCII("id_corge")));
// "id_dangling" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_dangling", "", &cache_entry));
EXPECT_FALSE(IsLink(pinned_directory_.AppendASCII("id_dangling")));
// "id_outside" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_outside", "", &cache_entry));
EXPECT_FALSE(IsLink(pinned_directory_.AppendASCII("id_outside")));
// "id_not_symlink" should be removed during cache initialization.
EXPECT_FALSE(metadata_->GetCacheEntry("id_not_symlink", "", &cache_entry));
EXPECT_FALSE(IsLink(pinned_directory_.AppendASCII("id_not_symlink")));
}
// Test DriveCacheMetadata::RemoveTemporaryFiles.
TEST_F(DriveCacheMetadataTest, RemoveTemporaryFiles) {
SetUpCacheMetadata();
DriveCacheMetadata::CacheMap cache_map;
{
DriveCacheEntry cache_entry;
cache_entry.set_md5("<md5>");
cache_entry.set_is_present(true);
InsertIntoMap(&cache_map, "<resource_id_1>", cache_entry);
}
{
DriveCacheEntry cache_entry;
cache_entry.set_md5("<md5>");
cache_entry.set_is_present(true);
cache_entry.set_is_persistent(true);
InsertIntoMap(&cache_map, "<resource_id_2>", cache_entry);
}
{
DriveCacheEntry cache_entry;
cache_entry.set_md5("<md5>");
cache_entry.set_is_present(true);
cache_entry.set_is_persistent(true);
InsertIntoMap(&cache_map, "<resource_id_3>", cache_entry);
}
{
DriveCacheEntry cache_entry;
cache_entry.set_md5("<md5>");
cache_entry.set_is_present(true);
InsertIntoMap(&cache_map, "<resource_id_4>", cache_entry);
}
AddAllMapEntries(cache_map);
metadata_->RemoveTemporaryFiles();
// resource 1 and 4 should be gone, as these are temporary.
DriveCacheEntry cache_entry;
EXPECT_FALSE(metadata_->GetCacheEntry("<resource_id_1>", "", &cache_entry));
EXPECT_TRUE(metadata_->GetCacheEntry("<resource_id_2>", "", &cache_entry));
EXPECT_TRUE(metadata_->GetCacheEntry("<resource_id_3>", "", &cache_entry));
EXPECT_FALSE(metadata_->GetCacheEntry("<resource_id_4>", "", &cache_entry));
}
TEST_F(DriveCacheMetadataTest, CorruptDB) {
SetUpCacheWithVariousFiles();
const FilePath db_path = cache_paths_[DriveCache::CACHE_TYPE_META].Append(
DriveCacheMetadata::kDriveCacheMetadataDBPath);
// Write a bogus file.
std::string text("Hello world");
file_util::WriteFile(db_path, text.c_str(), text.length());
SetUpCacheMetadata();
// "id_foo" is present and pinned.
DriveCacheEntry cache_entry;
ASSERT_TRUE(metadata_->GetCacheEntry("id_foo", "md5foo", &cache_entry));
EXPECT_EQ("md5foo", cache_entry.md5());
EXPECT_EQ(DriveCache::CACHE_TYPE_PERSISTENT,
DriveCache::GetSubDirectoryType(cache_entry));
}
} // namespace gdata