summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system.cc103
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system.h32
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc136
-rw-r--r--chrome/browser/chromeos/gdata/mock_gdata_file_system.h3
4 files changed, 271 insertions, 3 deletions
diff --git a/chrome/browser/chromeos/gdata/gdata_file_system.cc b/chrome/browser/chromeos/gdata/gdata_file_system.cc
index 2f7465f..3597cf5 100644
--- a/chrome/browser/chromeos/gdata/gdata_file_system.cc
+++ b/chrome/browser/chromeos/gdata/gdata_file_system.cc
@@ -2177,6 +2177,99 @@ GDataEntry* GDataFileSystem::GetGDataEntryByPath(
return entry;
}
+void GDataFileSystem::UpdateFileByResourceId(
+ const std::string& resource_id,
+ const FileOperationCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+ BrowserThread::CurrentlyOn(BrowserThread::IO));
+ RunTaskOnUIThread(
+ base::Bind(&GDataFileSystem::UpdateFileByResourceIdOnUIThread,
+ ui_weak_ptr_,
+ resource_id,
+ CreateRelayCallback(callback)));
+}
+
+void GDataFileSystem::UpdateFileByResourceIdOnUIThread(
+ const std::string& resource_id,
+ const FileOperationCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ GDataEntry* entry = root_->GetEntryByResourceId(resource_id);
+ if (!entry || !entry->AsGDataFile()) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ base::PLATFORM_FILE_ERROR_NOT_FOUND));
+ return;
+ }
+ GDataFile* file = entry->AsGDataFile();
+
+ cache_->GetFileOnUIThread(
+ resource_id,
+ file->file_md5(),
+ base::Bind(&GDataFileSystem::OnGetFileCompleteForUpdateFile,
+ ui_weak_ptr_,
+ callback));
+}
+
+void GDataFileSystem::OnGetFileCompleteForUpdateFile(
+ const FileOperationCallback& callback,
+ base::PlatformFileError error,
+ const std::string& resource_id,
+ const std::string& md5,
+ const FilePath& cache_file_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (error != base::PLATFORM_FILE_OK) {
+ if (!callback.is_null())
+ callback.Run(error);
+ return;
+ }
+
+ GDataEntry* entry = root_->GetEntryByResourceId(resource_id);
+ if (!entry || !entry->AsGDataFile()) {
+ if (!callback.is_null())
+ callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+ return;
+ }
+ GDataFile* file = entry->AsGDataFile();
+
+ uploader_->UploadExistingFile(
+ file->upload_url(),
+ file->GetFilePath(),
+ cache_file_path,
+ file->file_info().size,
+ file->content_mime_type(),
+ base::Bind(&GDataFileSystem::OnUpdatedFileUploaded,
+ ui_weak_ptr_,
+ callback));
+}
+
+void GDataFileSystem::OnUpdatedFileUploaded(
+ const FileOperationCallback& callback,
+ base::PlatformFileError error,
+ scoped_ptr<UploadFileInfo> upload_file_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(upload_file_info.get());
+
+ if (error != base::PLATFORM_FILE_OK) {
+ if (!callback.is_null())
+ callback.Run(error);
+ return;
+ }
+
+ // See comments in OnTransferCompleted() for why we copy this pointer.
+ const UploadFileInfo* upload_file_info_ptr = upload_file_info.get();
+ AddUploadedFile(upload_file_info_ptr->gdata_path.DirName(),
+ upload_file_info_ptr->entry.get(),
+ upload_file_info_ptr->file_path,
+ GDataCache::FILE_OPERATION_MOVE,
+ base::Bind(&OnAddUploadFileCompleted,
+ callback,
+ error,
+ base::Passed(&upload_file_info)));
+}
+
void GDataFileSystem::GetCacheState(const std::string& resource_id,
const std::string& md5,
const GetCacheStateCallback& callback) {
@@ -3379,6 +3472,16 @@ void GDataFileSystem::AddUploadedFileOnUIThread(
return;
}
+ // Remove an existing entry if present. This is needed when uploading a
+ // file to update an existing file, rather than adding a new file.
+ GDataEntry* existing_entry = root_->GetEntryByResourceId(
+ new_entry->resource_id());
+ if (existing_entry &&
+ // This should always match, but just in case.
+ existing_entry->parent() == parent_dir) {
+ parent_dir->RemoveEntry(existing_entry);
+ }
+
GDataFile* file = new_entry->AsGDataFile();
DCHECK(file);
const std::string& resource_id = file->resource_id();
diff --git a/chrome/browser/chromeos/gdata/gdata_file_system.h b/chrome/browser/chromeos/gdata/gdata_file_system.h
index f47a98e..b0c39ca 100644
--- a/chrome/browser/chromeos/gdata/gdata_file_system.h
+++ b/chrome/browser/chromeos/gdata/gdata_file_system.h
@@ -284,6 +284,16 @@ class GDataFileSystemInterface {
const GetFileCallback& get_file_callback,
const GetDownloadDataCallback& get_download_data_callback) = 0;
+ // Updates a file by the given |resource_id| on the gdata server by
+ // uploading an updated version. Used for uploading dirty files. The file
+ // should already be present in the cache.
+ //
+ // Can be called from UI/IO thread. |callback| and is run on the calling
+ // thread.
+ virtual void UpdateFileByResourceId(
+ const std::string& resource_id,
+ const FileOperationCallback& callback) = 0;
+
// Gets the cache state of file corresponding to |resource_id| and |md5| if it
// exists on disk.
// Initializes cache if it has not been initialized.
@@ -411,6 +421,9 @@ class GDataFileSystem : public GDataFileSystemInterface,
const std::string& resource_id,
const GetFileCallback& get_file_callback,
const GetDownloadDataCallback& get_download_data_callback) OVERRIDE;
+ virtual void UpdateFileByResourceId(
+ const std::string& resource_id,
+ const FileOperationCallback& callback) OVERRIDE;
virtual void GetCacheState(const std::string& resource_id,
const std::string& md5,
const GetCacheStateCallback& callback) OVERRIDE;
@@ -1071,6 +1084,22 @@ class GDataFileSystem : public GDataFileSystemInterface,
base::PlatformFileError error,
const GDataFileProto* file_proto);
+ // Called when GDataCache::GetFileOnUIThread() is completed for
+ // UpdateFileByResourceId().
+ void OnGetFileCompleteForUpdateFile(
+ const FileOperationCallback& callback,
+ base::PlatformFileError error,
+ const std::string& resource_id,
+ const std::string& md5,
+ const FilePath& cache_file_path);
+
+ // Called when GDataUploader::UploadUpdatedFile() is completed for
+ // UpdateFileByResourceId().
+ void OnUpdatedFileUploaded(
+ const FileOperationCallback& callback,
+ base::PlatformFileError error,
+ scoped_ptr<UploadFileInfo> upload_file_info);
+
// The following functions are used to forward calls to asynchronous public
// member functions to UI thread.
void SearchAsyncOnUIThread(const std::string& search_query,
@@ -1100,6 +1129,9 @@ class GDataFileSystem : public GDataFileSystemInterface,
const std::string& resource_id,
const GetFileCallback& get_file_callback,
const GetDownloadDataCallback& get_download_data_callback);
+ void UpdateFileByResourceIdOnUIThread(
+ const std::string& resource_id,
+ const FileOperationCallback& callback);
void GetEntryInfoByPathAsyncOnUIThread(
const FilePath& file_path,
const GetEntryInfoCallback& callback);
diff --git a/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc b/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc
index 709a256..9b065af 100644
--- a/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc
+++ b/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc
@@ -145,12 +145,28 @@ void DriveSearchCallback(
message_loop->Quit();
}
-// Action used to set mock expectations for GetDocuments.
+// Action used to set mock expectations for
+// DocumentsService::GetDocumentEntry().
ACTION_P2(MockGetDocumentEntryCallback, status, value) {
base::MessageLoopProxy::current()->PostTask(FROM_HERE,
base::Bind(arg1, status, base::Passed(value)));
}
+// Action used to set mock expectations for
+// GDataUploaderInterface::UploadExistingFile().
+ACTION_P4(MockUploadExistingFile,
+ error, gdata_path, local_file_path, document_entry) {
+ scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo);
+ upload_file_info->gdata_path = gdata_path;
+ upload_file_info->file_path = local_file_path;
+ upload_file_info->entry.reset(document_entry);
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE,
+ base::Bind(arg5, error, base::Passed(&upload_file_info)));
+
+ const int kUploadId = 123;
+ return kUploadId;
+}
+
} // anonymous namespace
class MockFreeDiskSpaceGetter : public FreeDiskSpaceGetterInterface {
@@ -358,8 +374,7 @@ class GDataFileSystemTest : public testing::Test {
}
GDataEntry* FindEntryByResourceId(const std::string& resource_id) {
- GDataEntry* entry = file_system_->root_->GetEntryByResourceId(resource_id);
- return entry ? entry->AsGDataFile() : NULL;
+ return file_system_->root_->GetEntryByResourceId(resource_id);
}
// Gets the entry info for |file_path| and compares the contents against
@@ -3357,6 +3372,121 @@ TEST_F(GDataFileSystemTest, GetFileByResourceId_FromCache) {
callback_helper_->download_path_.value());
}
+TEST_F(GDataFileSystemTest, UpdateFileByResourceId_PersistentFile) {
+ LoadRootFeedDocument("root_feed.json");
+
+ // This is a file defined in root_feed.json.
+ const FilePath kFilePath(FILE_PATH_LITERAL("drive/File 1.txt"));
+ const std::string kResourceId("file:2_file_resource_id");
+ const std::string kMd5("3b4382ebefec6e743578c76bbd0575ce");
+
+ // Pin the file so it'll be store in "persistent" directory.
+ EXPECT_CALL(*mock_sync_client_, OnCachePinned(kResourceId, kMd5)).Times(1);
+ TestPin(kResourceId,
+ kMd5,
+ base::PLATFORM_FILE_OK,
+ GDataCache::CACHE_STATE_PINNED,
+ GDataCache::CACHE_TYPE_PINNED);
+
+ // First store a file to cache. A cache file will be created at:
+ const FilePath cache_file_path =
+ GDataCache::GetCacheRootPath(profile_.get())
+ .AppendASCII("persistent")
+ .AppendASCII(kResourceId + "." + kMd5);
+ TestStoreToCache(kResourceId,
+ kMd5,
+ GetTestFilePath("root_feed.json"), // Anything works.
+ base::PLATFORM_FILE_OK,
+ GDataCache::CACHE_STATE_PRESENT |
+ GDataCache::CACHE_STATE_PINNED,
+ GDataCache::CACHE_TYPE_PERSISTENT);
+ ASSERT_TRUE(file_util::PathExists(cache_file_path));
+
+ // Create a DocumentEntry, which is needed to mock
+ // GDataUploaderInterface::UploadExistingFile().
+ // TODO(satorux): This should be cleaned up. crbug.com/134240.
+ DocumentEntry* document_entry = NULL;
+ scoped_ptr<base::Value> value(LoadJSONFile("root_feed.json"));
+ ASSERT_TRUE(value.get());
+ base::DictionaryValue* as_dict = NULL;
+ base::ListValue* entry_list = NULL;
+ if (value->GetAsDictionary(&as_dict) &&
+ as_dict->GetList("feed.entry", &entry_list)) {
+ for (size_t i = 0; i < entry_list->GetSize(); ++i) {
+ base::DictionaryValue* entry = NULL;
+ std::string resource_id;
+ if (entry_list->GetDictionary(i, &entry) &&
+ entry->GetString("gd$resourceId.$t", &resource_id) &&
+ resource_id == kResourceId) {
+ // This will be deleted by UploadExistingFile().
+ document_entry = DocumentEntry::CreateFrom(entry);
+ }
+ }
+ }
+ ASSERT_TRUE(document_entry);
+
+ // The mock uploader will be used to simulate a file upload.
+ EXPECT_CALL(*mock_uploader_, UploadExistingFile(
+ GURL("https://file_link_resumable_edit_media/"),
+ kFilePath,
+ cache_file_path,
+ 892721, // The size is written in the root_feed.json.
+ "audio/mpeg",
+ _)) // callback
+ .WillOnce(MockUploadExistingFile(
+ base::PLATFORM_FILE_OK,
+ FilePath::FromUTF8Unsafe("drive/File1"),
+ cache_file_path,
+ document_entry));
+
+ // We'll notify the directory change to the observer upon completion.
+ EXPECT_CALL(*mock_directory_observer_,
+ OnDirectoryChanged(Eq(FilePath(kGDataRootDirectory)))).Times(1);
+
+ // The callback will be called upon completion of
+ // UpdateFileByResourceId().
+ FileOperationCallback callback =
+ base::Bind(&CallbackHelper::FileOperationCallback,
+ callback_helper_.get());
+
+ // Check the number of files in the root directory. We'll compare the
+ // number after updating a file.
+ GDataEntry* root_entry =
+ FindEntryByResourceId(kGDataRootDirectoryResourceId);
+ ASSERT_TRUE(root_entry);
+ GDataDirectory* root_directory = root_entry->AsGDataDirectory();
+ ASSERT_TRUE(root_directory);
+ const size_t num_files_in_root = root_directory->child_files().size();
+
+ file_system_->UpdateFileByResourceId(kResourceId, callback);
+ RunAllPendingForIO(); // Get the file from the cache.
+ RunAllPendingForIO(); // Storing back the file to the cache.
+ EXPECT_EQ(base::PLATFORM_FILE_OK, callback_helper_->last_error_);
+ // Make sure that the number of files did not change (i.e. we updated an
+ // existing file, rather than adding a new file. The number of files
+ // increases if we don't handle the file update right).
+ EXPECT_EQ(num_files_in_root, root_directory->child_files().size());
+}
+
+TEST_F(GDataFileSystemTest, UpdateFileByResourceId_NonexistentFile) {
+ LoadRootFeedDocument("root_feed.json");
+
+ // This is nonexistent in root_feed.json.
+ const FilePath kFilePath(FILE_PATH_LITERAL("drive/Nonexistent.txt"));
+ const std::string kResourceId("file:nonexistent_resource_id");
+ const std::string kMd5("nonexistent_md5");
+
+ // The callback will be called upon completion of
+ // UpdateFileByResourceId().
+ FileOperationCallback callback =
+ base::Bind(&CallbackHelper::FileOperationCallback,
+ callback_helper_.get());
+
+ file_system_->UpdateFileByResourceId(kResourceId, callback);
+ RunAllPendingForIO(); // Get the file from the cache.
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_);
+}
+
TEST_F(GDataFileSystemTest, ContentSearch) {
LoadRootFeedDocument("root_feed.json");
diff --git a/chrome/browser/chromeos/gdata/mock_gdata_file_system.h b/chrome/browser/chromeos/gdata/mock_gdata_file_system.h
index 6954af4..7e36bd4 100644
--- a/chrome/browser/chromeos/gdata/mock_gdata_file_system.h
+++ b/chrome/browser/chromeos/gdata/mock_gdata_file_system.h
@@ -65,6 +65,9 @@ class MockGDataFileSystem : public GDataFileSystemInterface {
void(const std::string& resource_id,
const GetFileCallback& get_file_callback,
const GetDownloadDataCallback& get_download_data_callback));
+ MOCK_METHOD2(UpdateFileByResourceId,
+ void(const std::string& resource_id,
+ const FileOperationCallback& callback));
MOCK_METHOD3(GetCacheState, void(const std::string& resource_id,
const std::string& md5,
const GetCacheStateCallback& callback));