drive: Do not clear dirty bit if someone is writing to the file

Keeping the cache entry dirty to make sure that the edited file will eventually be uploaded.

BUG=326054
TEST=unit_tests

Review URL: https://codereview.chromium.org/139783004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245277 0039d316-1c4b-4281-b951-d872f2087c98
parent 41fc0392
...@@ -95,7 +95,9 @@ FileError UpdateFileLocalState( ...@@ -95,7 +95,9 @@ FileError UpdateFileLocalState(
if (drive_file_path->empty()) if (drive_file_path->empty())
return FILE_ERROR_NOT_FOUND; return FILE_ERROR_NOT_FOUND;
// Clear the dirty bit if we have updated an existing file. // Do not clear the dirty bit if someone is writing to the file.
if (cache->IsOpenedForWrite(local_id))
return FILE_ERROR_OK;
return cache->ClearDirty(local_id, entry.file_specific_info().md5()); return cache->ClearDirty(local_id, entry.file_specific_info().md5());
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "chrome/browser/chromeos/drive/file_system/update_operation.h" #include "chrome/browser/chromeos/drive/file_system/update_operation.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/file_util.h"
#include "base/md5.h"
#include "base/task_runner_util.h" #include "base/task_runner_util.h"
#include "chrome/browser/chromeos/drive/file_cache.h" #include "chrome/browser/chromeos/drive/file_cache.h"
#include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
...@@ -20,72 +22,68 @@ namespace file_system { ...@@ -20,72 +22,68 @@ namespace file_system {
class UpdateOperationTest : public OperationTestBase { class UpdateOperationTest : public OperationTestBase {
protected: protected:
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
OperationTestBase::SetUp(); OperationTestBase::SetUp();
operation_.reset(new UpdateOperation(blocking_task_runner(), operation_.reset(new UpdateOperation(blocking_task_runner(),
observer(), observer(),
scheduler(), scheduler(),
metadata(), metadata(),
cache())); cache()));
} }
scoped_ptr<UpdateOperation> operation_; // Stores |content| to the cache and mark it as dirty.
FileError StoreAndMarkDirty(const std::string& local_id,
const std::string& content) {
base::FilePath path;
if (!base::CreateTemporaryFileInDir(temp_dir(), &path) ||
!google_apis::test_util::WriteStringToFile(path, content))
return FILE_ERROR_FAILED;
// Store the file to cache.
FileError error = FILE_ERROR_FAILED;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::Store,
base::Unretained(cache()),
local_id, base::MD5String(content), path,
internal::FileCache::FILE_OPERATION_COPY),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
if (error != FILE_ERROR_OK)
return error;
// Add the dirty bit.
error = FILE_ERROR_FAILED;
scoped_ptr<base::ScopedClosureRunner> file_closer;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::OpenForWrite,
base::Unretained(cache()),
local_id,
&file_closer),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
return error;
}
scoped_ptr<UpdateOperation> operation_;
}; };
TEST_F(UpdateOperationTest, UpdateFileByLocalId_PersistentFile) { TEST_F(UpdateOperationTest, UpdateFileByLocalId) {
const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
const std::string kResourceId("file:2_file_resource_id"); const std::string kResourceId("file:2_file_resource_id");
const std::string kMd5("3b4382ebefec6e743578c76bbd0575ce");
const base::FilePath kTestFile = temp_dir().Append(FILE_PATH_LITERAL("foo"));
const std::string kTestFileContent = "I'm being uploaded! Yay!";
google_apis::test_util::WriteStringToFile(kTestFile, kTestFileContent);
const std::string local_id = GetLocalId(kFilePath); const std::string local_id = GetLocalId(kFilePath);
EXPECT_FALSE(local_id.empty()); EXPECT_FALSE(local_id.empty());
// Pin the file so it'll be store in "persistent" directory. const std::string kTestFileContent = "I'm being uploaded! Yay!";
FileError error = FILE_ERROR_FAILED; EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::Pin,
base::Unretained(cache()),
local_id),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
// First store a file to cache.
error = FILE_ERROR_FAILED;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::Store,
base::Unretained(cache()),
local_id, kMd5, kTestFile,
internal::FileCache::FILE_OPERATION_COPY),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
// Add the dirty bit.
error = FILE_ERROR_FAILED;
scoped_ptr<base::ScopedClosureRunner> file_closer;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::OpenForWrite,
base::Unretained(cache()),
local_id,
&file_closer),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
int64 original_changestamp = fake_service()->largest_changestamp(); int64 original_changestamp = fake_service()->largest_changestamp();
// The callback will be called upon completion of UpdateFileByLocalId(). // The callback will be called upon completion of UpdateFileByLocalId().
error = FILE_ERROR_FAILED; FileError error = FILE_ERROR_FAILED;
operation_->UpdateFileByLocalId( operation_->UpdateFileByLocalId(
local_id, local_id,
ClientContext(USER_INITIATED), ClientContext(USER_INITIATED),
...@@ -139,46 +137,17 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_NonexistentFile) { ...@@ -139,46 +137,17 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_NonexistentFile) {
TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) { TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) {
const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
const std::string kResourceId("file:2_file_resource_id"); const std::string kResourceId("file:2_file_resource_id");
const std::string kMd5("3b4382ebefec6e743578c76bbd0575ce");
const base::FilePath kTestFile = temp_dir().Append(FILE_PATH_LITERAL("foo"));
const std::string kTestFileContent = "I'm being uploaded! Yay!";
google_apis::test_util::WriteStringToFile(kTestFile, kTestFileContent);
const std::string local_id = GetLocalId(kFilePath); const std::string local_id = GetLocalId(kFilePath);
EXPECT_FALSE(local_id.empty()); EXPECT_FALSE(local_id.empty());
// First store a file to cache. const std::string kTestFileContent = "I'm being uploaded! Yay!";
FileError error = FILE_ERROR_FAILED; EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::Store,
base::Unretained(cache()),
local_id, kMd5, kTestFile,
internal::FileCache::FILE_OPERATION_COPY),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
// Add the dirty bit.
error = FILE_ERROR_FAILED;
scoped_ptr<base::ScopedClosureRunner> file_closer;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::OpenForWrite,
base::Unretained(cache()),
local_id,
&file_closer),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
int64 original_changestamp = fake_service()->largest_changestamp(); int64 original_changestamp = fake_service()->largest_changestamp();
// The callback will be called upon completion of UpdateFileByLocalId(). // The callback will be called upon completion of UpdateFileByLocalId().
error = FILE_ERROR_FAILED; FileError error = FILE_ERROR_FAILED;
operation_->UpdateFileByLocalId( operation_->UpdateFileByLocalId(
local_id, local_id,
ClientContext(USER_INITIATED), ClientContext(USER_INITIATED),
...@@ -218,6 +187,7 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) { ...@@ -218,6 +187,7 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) {
EXPECT_FALSE(cache_entry.is_dirty()); EXPECT_FALSE(cache_entry.is_dirty());
// Again mark the cache file dirty. // Again mark the cache file dirty.
scoped_ptr<base::ScopedClosureRunner> file_closer;
error = FILE_ERROR_FAILED; error = FILE_ERROR_FAILED;
base::PostTaskAndReplyWithResult( base::PostTaskAndReplyWithResult(
blocking_task_runner(), blocking_task_runner(),
...@@ -289,5 +259,81 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) { ...@@ -289,5 +259,81 @@ TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) {
EXPECT_LE(original_changestamp, fake_service()->largest_changestamp()); EXPECT_LE(original_changestamp, fake_service()->largest_changestamp());
} }
TEST_F(UpdateOperationTest, UpdateFileByLocalId_OpenedForWrite) {
const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
const std::string kResourceId("file:2_file_resource_id");
const std::string local_id = GetLocalId(kFilePath);
EXPECT_FALSE(local_id.empty());
const std::string kTestFileContent = "I'm being uploaded! Yay!";
EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
// Emulate a situation where someone is writing to the file.
scoped_ptr<base::ScopedClosureRunner> file_closer;
FileError error = FILE_ERROR_FAILED;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::OpenForWrite,
base::Unretained(cache()),
local_id,
&file_closer),
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
// Update. This should not clear the dirty bit.
error = FILE_ERROR_FAILED;
operation_->UpdateFileByLocalId(
local_id,
ClientContext(USER_INITIATED),
UpdateOperation::RUN_CONTENT_CHECK,
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
// Make sure that the cache is still dirty.
bool success = false;
FileCacheEntry cache_entry;
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::GetCacheEntry,
base::Unretained(cache()),
local_id,
&cache_entry),
google_apis::test_util::CreateCopyResultCallback(&success));
test_util::RunBlockingPoolTask();
EXPECT_TRUE(success);
EXPECT_TRUE(cache_entry.is_dirty());
// Close the file.
file_closer.reset();
// Update. This should clear the dirty bit.
error = FILE_ERROR_FAILED;
operation_->UpdateFileByLocalId(
local_id,
ClientContext(USER_INITIATED),
UpdateOperation::RUN_CONTENT_CHECK,
google_apis::test_util::CreateCopyResultCallback(&error));
test_util::RunBlockingPoolTask();
EXPECT_EQ(FILE_ERROR_OK, error);
// Make sure that the cache is no longer dirty.
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&internal::FileCache::GetCacheEntry,
base::Unretained(cache()),
local_id,
&cache_entry),
google_apis::test_util::CreateCopyResultCallback(&success));
test_util::RunBlockingPoolTask();
EXPECT_TRUE(success);
EXPECT_FALSE(cache_entry.is_dirty());
}
} // namespace file_system } // namespace file_system
} // namespace drive } // namespace drive
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment