summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-10 17:33:29 +0000
committersatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-10 17:33:29 +0000
commit6d32cb06edeb614f99568027e06a32c13c6eb6bb (patch)
tree29edbd4f53858cdfe234028d6f7a7da0211436d8
parent2cd60b80267486f4fcf362b13940a34217d2dfe3 (diff)
downloadchromium_src-6d32cb06edeb614f99568027e06a32c13c6eb6bb.zip
chromium_src-6d32cb06edeb614f99568027e06a32c13c6eb6bb.tar.gz
chromium_src-6d32cb06edeb614f99568027e06a32c13c6eb6bb.tar.bz2
gdata: Implement very simple cache eviction logic
The basic idea is to evict files from <gcache>/tmp as needed, but do not remove recursively as we don't want to touch <gache>/tmp/downloads, which is used for user initiated downloads like "Save As" Here's the behaviors added in this patch. Before downloading a file: - Check if we have enough space, based on the expected file size. - If we don't have enough space, remove files in <gcache>/tmp - If we still don't have enough space, return base::PLATFORM_FILE_ERROR_NO_SPACE - If we have enough space, start downloading the file After a file is downloaded: - At this point, the disk can be full or nearly full for various reasons. - If we don't have enough buffer space, - Remove the downloaded file - Remove files in <gcache>/tmp - Return base::PLATFORM_FILE_ERROR_NO_SPACE After a file is unpinned: - This is a chance to remove files in <gcache>/tmp - Try to remove files in <gcache>/tmp if we don't have enough space BUG=chromium-os:28553 TEST=added tests. Review URL: https://chromiumcodereview.appspot.com/10031001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@131578 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system.cc196
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system.h45
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc190
-rw-r--r--chrome/browser/chromeos/gdata/gdata_files.cc14
-rw-r--r--chrome/browser/chromeos/gdata/gdata_files.h3
-rw-r--r--chrome/browser/chromeos/gdata/gdata_files_unittest.cc51
-rw-r--r--chrome/chrome_tests.gypi1
7 files changed, 490 insertions, 10 deletions
diff --git a/chrome/browser/chromeos/gdata/gdata_file_system.cc b/chrome/browser/chromeos/gdata/gdata_file_system.cc
index 0213f86..a06a7a2 100644
--- a/chrome/browser/chromeos/gdata/gdata_file_system.cc
+++ b/chrome/browser/chromeos/gdata/gdata_file_system.cc
@@ -9,6 +9,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/chromeos/chromeos_version.h"
#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "base/json/json_file_value_serializer.h"
@@ -18,8 +19,10 @@
#include "base/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/platform_file.h"
+#include "base/sys_info.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread_restrictions.h"
#include "base/synchronization/waitable_event.h"
#include "base/values.h"
#include "chrome/browser/chromeos/gdata/gdata_documents_service.h"
@@ -71,6 +74,58 @@ const FilePath::CharType kAccountMetadataFile[] =
FILE_PATH_LITERAL("account_metadata.json");
const FilePath::CharType kSymLinkToDevNull[] = FILE_PATH_LITERAL("/dev/null");
+// Returns the home directory path, or an empty string if the home directory
+// is not found.
+// Copied from webkit/chromeos/cros_mount_point_provider.h.
+// TODO(satorux): Share the code.
+std::string GetHomeDirectory() {
+ if (base::chromeos::IsRunningOnChromeOS())
+ return "/home/chronos/user";
+
+ const char* home = getenv("HOME");
+ if (home)
+ return home;
+ return "";
+}
+
+// Used to tweak GetAmountOfFreeDiskSpace() behavior for testing.
+FreeDiskSpaceGetterInterface* global_free_disk_getter_for_testing = NULL;
+
+// Gets the amount of free disk space. Use
+// |global_free_disk_getter_for_testing| if set.
+int64 GetAmountOfFreeDiskSpace() {
+ if (global_free_disk_getter_for_testing)
+ return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace();
+
+ return base::SysInfo::AmountOfFreeDiskSpace(
+ FilePath::FromUTF8Unsafe(GetHomeDirectory()));
+}
+
+// Returns true if we have sufficient space to store the given number of
+// bytes, while keeping kMinFreeSpace bytes on the disk.
+bool HasEnoughSpaceFor(int64 num_bytes) {
+ int64 free_space = GetAmountOfFreeDiskSpace();
+ // Substract this as if this portion does not exist.
+ free_space -= kMinFreeSpace;
+ return (free_space >= num_bytes);
+}
+
+// Remove all files under the given directory, non-recursively.
+// Do not remove recursively as we don't want to touch <gache>/tmp/downloads,
+// which is used for user initiated downloads like "Save As"
+void RemoveAllFiles(const FilePath& directory) {
+ using file_util::FileEnumerator;
+
+ FileEnumerator enumerator(directory, false /* recursive */,
+ FileEnumerator::FILES);
+ for (FilePath file_path = enumerator.Next(); !file_path.empty();
+ file_path = enumerator.Next()) {
+ DVLOG(1) << "Removing " << file_path.value();
+ if (!file_util::Delete(file_path, false /* recursive */))
+ LOG(WARNING) << "Failed to delete " << file_path.value();
+ }
+}
+
// Converts gdata error code into file platform error code.
base::PlatformFileError GDataToPlatformError(GDataErrorCode status) {
switch (status) {
@@ -1492,7 +1547,77 @@ void GDataFileSystem::OnGetFileFromCache(const GetFileFromCacheParams& params,
return;
}
- // If cache file is not found, try to download it from the server instead.
+ // If cache file is not found, try to download the file from the server
+ // instead. This logic is rather complicated but here's how this works:
+ //
+ // Check if we have enough space, based on the expected file size.
+ // - if we don't have enough space, try to free up the disk space
+ // - if we still don't have enough space, return "no space" error
+ // - if we have enough space, start downloading the file from the server
+ int64 file_size = 0;
+ {
+ base::AutoLock lock(lock_); // To access the root directory.
+ GDataFileBase* file_base = root_->GetFileByResourceId(resource_id);
+ if (file_base)
+ file_size = file_base->file_info().size;
+ }
+
+ bool* has_enough_space = new bool(false);
+ PostBlockingPoolSequencedTaskAndReply(
+ kGDataFileSystemToken,
+ FROM_HERE,
+ base::Bind(&GDataFileSystem::FreeDiskSpaceIfNeededFor,
+ base::Unretained(this),
+ file_size,
+ has_enough_space),
+ base::Bind(&GDataFileSystem::StartDownloadFileIfEnoughSpace,
+ GetWeakPtrForCurrentThread(),
+ params,
+ cache_file_path,
+ base::Owned(has_enough_space)));
+}
+
+void GDataFileSystem::FreeDiskSpaceIfNeededFor(int64 num_bytes,
+ bool* has_enough_space) {
+ // Do nothing and return if we have enough space.
+ *has_enough_space = HasEnoughSpaceFor(num_bytes);
+ if (*has_enough_space)
+ return;
+
+ // Otherwise, try to free up the disk space.
+ DVLOG(1) << "Freeing up disk space for " << num_bytes;
+ base::AutoLock lock(lock_); // To access the cache map.
+ // First remove temporary files from the cache map.
+ root_->RemoveTemporaryFilesFromCacheMap();
+ // Then remove all files under "tmp" directory.
+ RemoveAllFiles(GetGDataCacheTmpDirectory());
+
+ // Check the disk space again.
+ *has_enough_space = HasEnoughSpaceFor(num_bytes);
+}
+
+void GDataFileSystem::FreeDiskSpaceIfNeeded(bool* has_enough_space) {
+ FreeDiskSpaceIfNeededFor(0, has_enough_space);
+}
+
+void GDataFileSystem::StartDownloadFileIfEnoughSpace(
+ const GetFileFromCacheParams& params,
+ const FilePath& cache_file_path,
+ bool* has_enough_space) {
+ if (!*has_enough_space) {
+ // If no enough space, return PLATFORM_FILE_ERROR_NO_SPACE.
+ if (!params.callback.is_null()) {
+ params.proxy->PostTask(FROM_HERE,
+ base::Bind(params.callback,
+ base::PLATFORM_FILE_ERROR_NO_SPACE,
+ cache_file_path,
+ params.mime_type,
+ REGULAR_FILE));
+ }
+ return;
+ }
+
+ // We have enough disk space. Start downloading the file.
documents_service_->DownloadFile(
params.virtual_file_path,
params.local_tmp_path,
@@ -2304,18 +2429,60 @@ void GDataFileSystem::OnFileDownloaded(
GDataErrorCode status,
const GURL& content_url,
const FilePath& downloaded_file_path) {
+ // At this point, the disk can be full or nearly full for several reasons:
+ // - The expected file size was incorrect and the file was larger
+ // - There was an in-flight download operation and it used up space
+ // - The disk became full for some user actions we cannot control
+ // (ex. the user might have downloaded a large file from a regular web site)
+ //
+ // If we don't have enough space, we return PLATFORM_FILE_ERROR_NO_SPACE,
+ // and try to free up space, even if the file was downloaded successfully.
+ bool* has_enough_space = new bool(false);
+ PostBlockingPoolSequencedTaskAndReply(
+ kGDataFileSystemToken,
+ FROM_HERE,
+ base::Bind(&GDataFileSystem::FreeDiskSpaceIfNeeded,
+ base::Unretained(this),
+ has_enough_space),
+ base::Bind(&GDataFileSystem::OnFileDownloadedAndSpaceChecked,
+ GetWeakPtrForCurrentThread(),
+ params,
+ status,
+ content_url,
+ downloaded_file_path,
+ base::Owned(has_enough_space)));
+}
+
+void GDataFileSystem::OnFileDownloadedAndSpaceChecked(
+ const GetFileFromCacheParams& params,
+ GDataErrorCode status,
+ const GURL& content_url,
+ const FilePath& downloaded_file_path,
+ bool* has_enough_space) {
base::PlatformFileError error = GDataToPlatformError(status);
// Make sure that downloaded file is properly stored in cache. We don't have
// to wait for this operation to finish since the user can already use the
// downloaded file.
if (error == base::PLATFORM_FILE_OK) {
- StoreToCache(params.resource_id,
- params.md5,
- downloaded_file_path,
- FILE_OPERATION_MOVE,
- base::Bind(&GDataFileSystem::OnDownloadStoredToCache,
- GetWeakPtrForCurrentThread()));
+ if (*has_enough_space) {
+ StoreToCache(params.resource_id,
+ params.md5,
+ downloaded_file_path,
+ FILE_OPERATION_MOVE,
+ base::Bind(&GDataFileSystem::OnDownloadStoredToCache,
+ GetWeakPtrForCurrentThread()));
+ } else {
+ // If we don't have enough space, remove the downloaded file, and
+ // report "no space" error.
+ PostBlockingPoolSequencedTask(
+ kGDataFileSystemToken,
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&file_util::Delete),
+ downloaded_file_path,
+ false /* recursive*/));
+ error = base::PLATFORM_FILE_ERROR_NO_SPACE;
+ }
}
if (!params.callback.is_null()) {
@@ -3758,6 +3925,16 @@ void GDataFileSystem::OnFileUnpinned(base::PlatformFileError* error,
if (*error == base::PLATFORM_FILE_OK)
NotifyFileUnpinned(resource_id, md5);
+
+ // Now the file is moved from "persistent" to "tmp" directory.
+ // It's a chance to free up space if needed.
+ bool* has_enough_space = new bool(false);
+ PostBlockingPoolSequencedTask(
+ kGDataFileSystemToken,
+ FROM_HERE,
+ base::Bind(&GDataFileSystem::FreeDiskSpaceIfNeeded,
+ base::Unretained(this),
+ base::Owned(has_enough_space)));
}
//============= GDataFileSystem: internal helper functions =====================
@@ -3938,4 +4115,9 @@ void GDataFileSystem::InitializePreferenceObserver() {
pref_registrar_->Add(prefs::kDisableGDataHostedFiles, this);
}
+void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
+ delete global_free_disk_getter_for_testing; // Safe to delete NULL;
+ global_free_disk_getter_for_testing = getter;
+}
+
} // namespace gdata
diff --git a/chrome/browser/chromeos/gdata/gdata_file_system.h b/chrome/browser/chromeos/gdata/gdata_file_system.h
index 5ef450c..27621db 100644
--- a/chrome/browser/chromeos/gdata/gdata_file_system.h
+++ b/chrome/browser/chromeos/gdata/gdata_file_system.h
@@ -793,6 +793,15 @@ class GDataFileSystem : public GDataFileSystemInterface,
const GURL& content_url,
const FilePath& downloaded_file_path);
+ // Similar to OnFileDownloaded() but takes |has_enough_space| so we report
+ // an error in case we don't have enough disk space.
+ void OnFileDownloadedAndSpaceChecked(
+ const GetFileFromCacheParams& params,
+ GDataErrorCode status,
+ const GURL& content_url,
+ const FilePath& downloaded_file_path,
+ bool* has_enough_space);
+
// Callback for handling internal StoreToCache() calls after downloading
// file content.
void OnDownloadStoredToCache(base::PlatformFileError error,
@@ -1218,6 +1227,22 @@ class GDataFileSystem : public GDataFileSystemInterface,
const FilePath& gdata_file_path,
const FilePath& cache_file_path);
+ // Frees up disk space to store the given number of bytes, while keeping
+ // kMinFreSpace bytes on the disk, if needed. |has_enough_space| is
+ // updated to indicate if we have enough space.
+ void FreeDiskSpaceIfNeededFor(int64 num_bytes,
+ bool* has_enough_space);
+
+ // Frees up disk space if we have less than kMinFreSpace. |has_enough_space|
+ // is updated to indicate if we have enough space.
+ void FreeDiskSpaceIfNeeded(bool* has_enough_space);
+
+ // Starts downloading a file if we have enough disk space indicated by
+ // |has_enough_space|.
+ void StartDownloadFileIfEnoughSpace(const GetFileFromCacheParams& params,
+ const FilePath& cache_file_path,
+ bool* has_enough_space);
+
// Cache internal helper functions.
// Unsafe (unlocked) version of InitializeCacheIfnecessary method.
@@ -1318,6 +1343,26 @@ class GDataFileSystem : public GDataFileSystemInterface,
ObserverList<Observer> observers_;
};
+// The minimum free space to keep. GDataFileSystem::GetFile() returns
+// base::PLATFORM_FILE_ERROR_NO_SPACE if the available space is smaller than
+// this value.
+//
+// Copied from cryptohome/homedirs.h.
+// TODO(satorux): Share the constant.
+const int64 kMinFreeSpace = 512 * 1LL << 20;
+
+// Interface class used for getting the free disk space. Only for testing.
+class FreeDiskSpaceGetterInterface {
+ public:
+ virtual ~FreeDiskSpaceGetterInterface() {}
+ virtual int64 AmountOfFreeDiskSpace() const = 0;
+};
+
+// Sets the free disk space getter for testing.
+// The existing getter is deleted.
+void SetFreeDiskSpaceGetterForTesting(
+ FreeDiskSpaceGetterInterface* getter);
+
} // namespace gdata
#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_FILE_SYSTEM_H_
diff --git a/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc b/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc
index 78edaaa..fc7c093 100644
--- a/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc
+++ b/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc
@@ -33,6 +33,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AnyNumber;
+using ::testing::AtLeast;
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::Ne;
@@ -99,6 +100,8 @@ struct InitialCacheResource {
"local", gdata::GDataRootDirectory::CACHE_TYPE_PERSISTENT },
};
+const int64 kLotsOfSpace = gdata::kMinFreeSpace * 10;
+
struct PathToVerify {
PathToVerify(const FilePath& in_path_to_scan,
const FilePath& in_expected_existing_path) :
@@ -114,6 +117,12 @@ struct PathToVerify {
namespace gdata {
+class MockFreeDiskSpaceGetter : public FreeDiskSpaceGetterInterface {
+ public:
+ virtual ~MockFreeDiskSpaceGetter() {}
+ MOCK_CONST_METHOD0(AmountOfFreeDiskSpace, int64());
+};
+
class GDataFileSystemTest : public testing::Test {
protected:
GDataFileSystemTest()
@@ -144,6 +153,10 @@ class GDataFileSystemTest : public testing::Test {
EXPECT_CALL(*mock_doc_service_, Initialize(profile_.get())).Times(1);
+ // Likewise, this will be owned by GDataFileSystem.
+ mock_free_disk_space_checker_ = new MockFreeDiskSpaceGetter;
+ SetFreeDiskSpaceGetterForTesting(mock_free_disk_space_checker_);
+
ASSERT_FALSE(file_system_);
file_system_ = new GDataFileSystem(profile_.get(),
mock_doc_service_);
@@ -158,6 +171,7 @@ class GDataFileSystemTest : public testing::Test {
EXPECT_CALL(*mock_doc_service_, CancelAll()).Times(1);
delete file_system_;
file_system_ = NULL;
+ SetFreeDiskSpaceGetterForTesting(NULL);
// Run the remaining tasks on the main thread, so that reply tasks (2nd
// callback of PostTaskAndReply) are run. Otherwise, there will be a leak
@@ -259,6 +273,25 @@ class GDataFileSystemTest : public testing::Test {
file_origin);
}
+ // Returns true if the cache entry exists for the given resource ID and MD5.
+ bool CacheEntryExists(const std::string& resource_id,
+ const std::string& md5) {
+ GDataRootDirectory::CacheEntry* entry =
+ file_system_->root_->GetCacheEntry(resource_id, md5);
+ return entry != NULL;
+ }
+
+ // Returns true if the cache file exists for the given resource ID and MD5.
+ bool CacheFileExists(const std::string& resource_id,
+ const std::string& md5) {
+ const FilePath file_path = file_system_->GetCacheFilePath(
+ resource_id,
+ md5,
+ GDataRootDirectory::CACHE_TYPE_TMP,
+ GDataFileSystem::CACHED_FILE_FROM_SERVER);
+ return file_util::PathExists(file_path);
+ }
+
void TestGetCacheFilePath(const std::string& resource_id,
const std::string& md5,
const std::string& expected_filename) {
@@ -875,6 +908,7 @@ class GDataFileSystemTest : public testing::Test {
scoped_refptr<CallbackHelper> callback_helper_;
GDataFileSystem* file_system_;
MockDocumentsService* mock_doc_service_;
+ MockFreeDiskSpaceGetter* mock_free_disk_space_checker_;
scoped_ptr<MockGDataSyncClient> mock_sync_client_;
int num_callback_invocations_;
@@ -1840,6 +1874,8 @@ TEST_F(GDataFileSystemTest, RemoveFromCacheSimple) {
TEST_F(GDataFileSystemTest, PinAndUnpin) {
EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
std::string resource_id("pdf:1a2b");
std::string md5("abcdef0123456789");
@@ -2077,6 +2113,8 @@ TEST_F(GDataFileSystemTest, DirtyCachePinned) {
TEST_F(GDataFileSystemTest, PinAndUnpinDirtyCache) {
EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
std::string resource_id("pdf:1a2b");
std::string md5("abcdef0123456789");
@@ -2430,7 +2468,7 @@ TEST_F(GDataFileSystemTest, CreateDirectoryWithService) {
// EXPECT_EQ(base::PLATFORM_FILE_OK, callback_helper_->last_error_);
}
-TEST_F(GDataFileSystemTest, GetFile_FromGData) {
+TEST_F(GDataFileSystemTest, GetFile_FromGData_EnoughSpace) {
EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
LoadRootFeedDocument("root_feed.json");
@@ -2443,6 +2481,11 @@ TEST_F(GDataFileSystemTest, GetFile_FromGData) {
GDataFileBase* file_base = FindFile(file_in_root);
GDataFile* file = file_base->AsGDataFile();
FilePath downloaded_file = GetCachePathForFile(file);
+ const int64 file_size = file_base->file_info().size;
+
+ // Pretend we have enough space.
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .Times(2).WillRepeatedly(Return(file_size + kMinFreeSpace));
// The file is obtained with the mock DocumentsService.
EXPECT_CALL(*mock_doc_service_,
@@ -2453,13 +2496,150 @@ TEST_F(GDataFileSystemTest, GetFile_FromGData) {
.Times(1);
file_system_->GetFile(file_in_root, callback);
- RunAllPendingForIO();
+ RunAllPendingForIO(); // Try to get from the cache.
+ RunAllPendingForIO(); // Check if we have space before downloading.
+ RunAllPendingForIO(); // Check if we have space after downloading.
+ EXPECT_EQ(base::PLATFORM_FILE_OK, callback_helper_->last_error_);
EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_);
EXPECT_EQ(downloaded_file.value(),
callback_helper_->download_path_.value());
}
+TEST_F(GDataFileSystemTest, GetFile_FromGData_NoSpaceAtAll) {
+ EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
+
+ LoadRootFeedDocument("root_feed.json");
+
+ GetFileCallback callback =
+ base::Bind(&CallbackHelper::GetFileCallback,
+ callback_helper_.get());
+
+ FilePath file_in_root(FILE_PATH_LITERAL("gdata/File 1.txt"));
+ GDataFileBase* file_base = FindFile(file_in_root);
+ GDataFile* file = file_base->AsGDataFile();
+ FilePath downloaded_file = GetCachePathForFile(file);
+
+ // Pretend we have no space at all.
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .Times(2).WillRepeatedly(Return(0));
+
+ // The file is not obtained with the mock DocumentsService, because of no
+ // space.
+ EXPECT_CALL(*mock_doc_service_,
+ DownloadFile(file_in_root,
+ downloaded_file,
+ GURL("https://file_content_url/"),
+ _))
+ .Times(0);
+
+ file_system_->GetFile(file_in_root, callback);
+ RunAllPendingForIO(); // Try to get from the cache.
+ RunAllPendingForIO(); // Check if we have space before downloading.
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ callback_helper_->last_error_);
+}
+
+TEST_F(GDataFileSystemTest, GetFile_FromGData_NoEnoughSpaceButCanFreeUp) {
+ EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
+
+ LoadRootFeedDocument("root_feed.json");
+
+ GetFileCallback callback =
+ base::Bind(&CallbackHelper::GetFileCallback,
+ callback_helper_.get());
+
+ FilePath file_in_root(FILE_PATH_LITERAL("gdata/File 1.txt"));
+ GDataFileBase* file_base = FindFile(file_in_root);
+ GDataFile* file = file_base->AsGDataFile();
+ FilePath downloaded_file = GetCachePathForFile(file);
+ const int64 file_size = file_base->file_info().size;
+
+ // Pretend we have no space first (checked before downloading a file),
+ // but then start reporting we have space. This is to emulate that
+ // the disk space was freed up by removing temporary files.
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .WillOnce(Return(0))
+ .WillOnce(Return(file_size + kMinFreeSpace))
+ .WillOnce(Return(file_size + kMinFreeSpace));
+
+ // Store something in the temporary cache directory.
+ TestStoreToCache("<resource_id>",
+ "<md5>",
+ GetTestFilePath("root_feed.json"),
+ base::PLATFORM_FILE_OK,
+ GDataFile::CACHE_STATE_PRESENT,
+ GDataRootDirectory::CACHE_TYPE_TMP);
+ ASSERT_TRUE(CacheEntryExists("<resource_id>", "<md5>"));
+ ASSERT_TRUE(CacheFileExists("<resource_id>", "<md5>"));
+
+ // The file is obtained with the mock DocumentsService, because of we freed
+ // up the space.
+ EXPECT_CALL(*mock_doc_service_,
+ DownloadFile(file_in_root,
+ downloaded_file,
+ GURL("https://file_content_url/"),
+ _))
+ .Times(1);
+
+ file_system_->GetFile(file_in_root, callback);
+ RunAllPendingForIO(); // Try to get from the cache.
+ RunAllPendingForIO(); // Check if we have space before downloading.
+ RunAllPendingForIO(); // Check if we have space after downloading
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK, callback_helper_->last_error_);
+ EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_);
+ EXPECT_EQ(downloaded_file.value(),
+ callback_helper_->download_path_.value());
+
+ // The file should be removed in order to free up space, and the cache
+ // entry should also be removed.
+ ASSERT_FALSE(CacheEntryExists("<resource_id>", "<md5>"));
+ ASSERT_FALSE(CacheFileExists("<resource_id>", "<md5>"));
+}
+
+TEST_F(GDataFileSystemTest, GetFile_FromGData_EnoughSpaceButBecomeFull) {
+ EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
+
+ LoadRootFeedDocument("root_feed.json");
+
+ GetFileCallback callback =
+ base::Bind(&CallbackHelper::GetFileCallback,
+ callback_helper_.get());
+
+ FilePath file_in_root(FILE_PATH_LITERAL("gdata/File 1.txt"));
+ GDataFileBase* file_base = FindFile(file_in_root);
+ GDataFile* file = file_base->AsGDataFile();
+ FilePath downloaded_file = GetCachePathForFile(file);
+ const int64 file_size = file_base->file_info().size;
+
+ // Pretend we have enough space first (checked before downloading a file),
+ // but then start reporting we have not enough space. This is to emulate that
+ // the disk space becomes full after the file is downloaded for some reason
+ // (ex. the actual file was larger than the expected size).
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .WillOnce(Return(file_size + kMinFreeSpace))
+ .WillOnce(Return(kMinFreeSpace - 1))
+ .WillOnce(Return(kMinFreeSpace - 1));
+
+ // The file is obtained with the mock DocumentsService.
+ EXPECT_CALL(*mock_doc_service_,
+ DownloadFile(file_in_root,
+ downloaded_file,
+ GURL("https://file_content_url/"),
+ _))
+ .Times(1);
+
+ file_system_->GetFile(file_in_root, callback);
+ RunAllPendingForIO(); // Try to get from the cache.
+ RunAllPendingForIO(); // Check if we have space before downloading.
+ RunAllPendingForIO(); // Check if we have space after downloading.
+
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ callback_helper_->last_error_);
+}
+
TEST_F(GDataFileSystemTest, GetFile_FromCache) {
EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
@@ -2536,6 +2716,8 @@ TEST_F(GDataFileSystemTest, GetFile_HostedDocument) {
TEST_F(GDataFileSystemTest, GetFileForResourceId) {
EXPECT_CALL(*mock_sync_client_, OnCacheInitialized()).Times(1);
+ EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+ .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
LoadRootFeedDocument("root_feed.json");
@@ -2559,7 +2741,9 @@ TEST_F(GDataFileSystemTest, GetFileForResourceId) {
file_system_->GetFileForResourceId(file->resource_id(),
callback);
- RunAllPendingForIO();
+ RunAllPendingForIO(); // Try to get from the cache.
+ RunAllPendingForIO(); // Check if we have space before downloading.
+ RunAllPendingForIO(); // Check if we have space after downloading.
EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_);
EXPECT_EQ(downloaded_file.value(),
diff --git a/chrome/browser/chromeos/gdata/gdata_files.cc b/chrome/browser/chromeos/gdata/gdata_files.cc
index 7bddede..c1cc930 100644
--- a/chrome/browser/chromeos/gdata/gdata_files.cc
+++ b/chrome/browser/chromeos/gdata/gdata_files.cc
@@ -507,4 +507,18 @@ void GDataRootDirectory::GetCacheState(
file_system_->GetCacheState(resource_id, md5, callback);
}
+void GDataRootDirectory::RemoveTemporaryFilesFromCacheMap() {
+ CacheMap::iterator iter = cache_map_.begin();
+ while (iter != cache_map_.end()) {
+ CacheEntry* entry = iter->second;
+ if (entry->sub_dir_type == CACHE_TYPE_TMP) {
+ delete entry;
+ // Post-increment the iterator to avoid iterator invalidation.
+ cache_map_.erase(iter++);
+ } else {
+ ++iter;
+ }
+ }
+}
+
} // namespace gdata
diff --git a/chrome/browser/chromeos/gdata/gdata_files.h b/chrome/browser/chromeos/gdata/gdata_files.h
index 105c6e8..5ca3457 100644
--- a/chrome/browser/chromeos/gdata/gdata_files.h
+++ b/chrome/browser/chromeos/gdata/gdata_files.h
@@ -394,6 +394,9 @@ class GDataRootDirectory : public GDataDirectory {
const std::string& md5,
const GetCacheStateCallback& callback);
+ // Remove temporary files (files in CACHE_TYPE_TMP) from the cache map.
+ void RemoveTemporaryFilesFromCacheMap();
+
private:
ResourceMap resource_map_;
CacheMap cache_map_;
diff --git a/chrome/browser/chromeos/gdata/gdata_files_unittest.cc b/chrome/browser/chromeos/gdata/gdata_files_unittest.cc
new file mode 100644
index 0000000..d6d3341
--- /dev/null
+++ b/chrome/browser/chromeos/gdata/gdata_files_unittest.cc
@@ -0,0 +1,51 @@
+// 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/gdata_files.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gdata {
+
+TEST(GDataRootDirectoryTest, RemoveTemporaryFilesFromCacheMap) {
+ scoped_ptr<GDataRootDirectory> root(new GDataRootDirectory(NULL));
+ GDataRootDirectory::CacheMap cache_map;
+ cache_map.insert(std::make_pair(
+ "<resource_id_1>",
+ new GDataRootDirectory::CacheEntry(
+ "<md5>",
+ GDataRootDirectory::CACHE_TYPE_TMP,
+ GDataFile::CACHE_STATE_NONE)));
+ cache_map.insert(std::make_pair(
+ "<resource_id_2>",
+ new GDataRootDirectory::CacheEntry(
+ "<md5>",
+ GDataRootDirectory::CACHE_TYPE_PINNED,
+ GDataFile::CACHE_STATE_NONE)));
+ cache_map.insert(std::make_pair(
+ "<resource_id_3>",
+ new GDataRootDirectory::CacheEntry(
+ "<md5>",
+ GDataRootDirectory::CACHE_TYPE_OUTGOING,
+ GDataFile::CACHE_STATE_NONE)));
+ cache_map.insert(std::make_pair(
+ "<resource_id_4>",
+ new GDataRootDirectory::CacheEntry(
+ "<md5>",
+ GDataRootDirectory::CACHE_TYPE_TMP,
+ GDataFile::CACHE_STATE_NONE)));
+ root->SetCacheMap(cache_map);
+ root->RemoveTemporaryFilesFromCacheMap();
+ // resource 1 and 4 should be gone, as these are CACHE_TYPE_TMP.
+ ASSERT_TRUE(root->GetCacheEntry("<resource_id_1>", "") == NULL);
+ ASSERT_TRUE(root->GetCacheEntry("<resource_id_2>", "") != NULL);
+ ASSERT_TRUE(root->GetCacheEntry("<resource_id_3>", "") != NULL);
+ ASSERT_TRUE(root->GetCacheEntry("<resource_id_4>", "") == NULL);
+
+}
+
+} // namespace gdata
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 9740305..1230540 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1274,6 +1274,7 @@
'browser/chromeos/extensions/file_browser_notifications_unittest.cc',
'browser/chromeos/external_metrics_unittest.cc',
'browser/chromeos/gdata/gdata_file_system_unittest.cc',
+ 'browser/chromeos/gdata/gdata_files_unittest.cc',
'browser/chromeos/gdata/gdata_operation_registry_unittest.cc',
'browser/chromeos/gdata/gdata_parser_unittest.cc',
'browser/chromeos/gdata/gdata_sync_client_unittest.cc',