drive: Update parent entry first when appropriate

EntryUpdatePerformer does nothing if the parent is a locally created entry waiting for update.
SyncClient adds update tasks for child entries.
Add ResourceMetadata::ReadDirectoryById

BUG=260540
TEST=unit_tests
[email protected]

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248441 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/chromeos/drive/resource_metadata.cc b/chrome/browser/chromeos/drive/resource_metadata.cc
index 2c873a4..6fca4ce1 100644
--- a/chrome/browser/chromeos/drive/resource_metadata.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata.cc
@@ -312,9 +312,17 @@
   FileError error = GetIdByPath(path, &id);
   if (error != FILE_ERROR_OK)
     return error;
+  return ReadDirectoryById(id, out_entries);
+}
+
+FileError ResourceMetadata::ReadDirectoryById(
+    const std::string& id,
+    ResourceEntryVector* out_entries) {
+  DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+  DCHECK(out_entries);
 
   ResourceEntry entry;
-  error = GetResourceEntryById(id, &entry);
+  FileError error = GetResourceEntryById(id, &entry);
   if (error != FILE_ERROR_OK)
     return error;
 
diff --git a/chrome/browser/chromeos/drive/resource_metadata.h b/chrome/browser/chromeos/drive/resource_metadata.h
index e669114..52b5d41 100644
--- a/chrome/browser/chromeos/drive/resource_metadata.h
+++ b/chrome/browser/chromeos/drive/resource_metadata.h
@@ -97,6 +97,10 @@
   FileError ReadDirectoryByPath(const base::FilePath& file_path,
                                 ResourceEntryVector* out_entries);
 
+  // Finds and reads a directory by |id|.
+  FileError ReadDirectoryById(const std::string& id,
+                              ResourceEntryVector* out_entries);
+
   // Replaces an existing entry with the same local ID as |entry|.
   FileError RefreshEntry(const ResourceEntry& entry);
 
diff --git a/chrome/browser/chromeos/drive/sync/entry_update_performer.cc b/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
index b9a50cdeb..ec73b32a 100644
--- a/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
+++ b/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
@@ -237,6 +237,14 @@
     return;
   }
 
+  // Parent was locally created and needs update. Just return for now.
+  // This entry should be updated again after the parent update completes.
+  if (local_state->parent_entry.resource_id().empty() &&
+      local_state->parent_entry.metadata_edit_state() != ResourceEntry::CLEAN) {
+    callback.Run(FILE_ERROR_OK);
+    return;
+  }
+
   base::Time last_modified = base::Time::FromInternalValue(
       local_state->entry.file_info().last_modified());
   base::Time last_accessed = base::Time::FromInternalValue(
@@ -290,6 +298,7 @@
     return;
   }
 
+  // Create directory.
   if (local_state->entry.file_info().is_directory() &&
       local_state->entry.resource_id().empty()) {
     // Lock the loader to avoid race conditions.
diff --git a/chrome/browser/chromeos/drive/sync_client.cc b/chrome/browser/chromeos/drive/sync_client.cc
index d4488a2..5ab920f0 100644
--- a/chrome/browser/chromeos/drive/sync_client.cc
+++ b/chrome/browser/chromeos/drive/sync_client.cc
@@ -397,6 +397,17 @@
 
   if (error == FILE_ERROR_OK) {
     DVLOG(1) << "Updated " << local_id;
+
+    // Add update tasks for child entries which may be waiting for the parent to
+    // be updated.
+    ResourceEntryVector* entries = new ResourceEntryVector;
+    base::PostTaskAndReplyWithResult(
+        blocking_task_runner_.get(),
+        FROM_HERE,
+        base::Bind(&ResourceMetadata::ReadDirectoryById,
+                   base::Unretained(metadata_), local_id, entries),
+        base::Bind(&SyncClient::AddChildUpdateTasks,
+                   weak_ptr_factory_.GetWeakPtr(), base::Owned(entries)));
   } else {
     switch (error) {
       case FILE_ERROR_ABORT:
@@ -424,5 +435,19 @@
   }
 }
 
+void SyncClient::AddChildUpdateTasks(const ResourceEntryVector* entries,
+                                     FileError error) {
+  if (error != FILE_ERROR_OK)
+    return;
+
+  for (size_t i = 0; i < entries->size(); ++i) {
+    const ResourceEntry& entry = (*entries)[i];
+    if (entry.metadata_edit_state() != ResourceEntry::CLEAN) {
+      AddUpdateTaskInternal(ClientContext(BACKGROUND), entry.local_id(),
+                            base::TimeDelta::FromSeconds(0));
+    }
+  }
+}
+
 }  // namespace internal
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/sync_client.h b/chrome/browser/chromeos/drive/sync_client.h
index 500f21d..3e9c574c 100644
--- a/chrome/browser/chromeos/drive/sync_client.h
+++ b/chrome/browser/chromeos/drive/sync_client.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/drive/file_errors.h"
+#include "chrome/browser/chromeos/drive/resource_metadata.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -134,19 +135,18 @@
   bool OnTaskComplete(SyncType type, const std::string& local_id);
 
   // Called when the file for |local_id| is fetched.
-  // Calls DoSyncLoop() to go back to the sync loop.
   void OnFetchFileComplete(const std::string& local_id,
                            FileError error,
                            const base::FilePath& local_path,
                            scoped_ptr<ResourceEntry> entry);
 
-  // Called when the file for |local_id| is uploaded.
-  // Calls DoSyncLoop() to go back to the sync loop.
-  void OnUploadFileComplete(const std::string& local_id, FileError error);
-
   // Called when the entry is updated.
   void OnUpdateComplete(const std::string& local_id, FileError error);
 
+  // Adds update tasks for |entries|.
+  void AddChildUpdateTasks(const ResourceEntryVector* entries,
+                           FileError error);
+
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
   file_system::OperationObserver* operation_observer_;
   ResourceMetadata* metadata_;
diff --git a/chrome/browser/chromeos/drive/sync_client_unittest.cc b/chrome/browser/chromeos/drive/sync_client_unittest.cc
index fcfbfe8..e9e55c6 100644
--- a/chrome/browser/chromeos/drive/sync_client_unittest.cc
+++ b/chrome/browser/chromeos/drive/sync_client_unittest.cc
@@ -432,5 +432,44 @@
   EXPECT_EQ(2, drive_service_->download_file_count());
 }
 
+TEST_F(SyncClientTest, Dependencies) {
+  // Create directories locally.
+  const base::FilePath kPath1(FILE_PATH_LITERAL("drive/root/dir1"));
+  const base::FilePath kPath2 = kPath1.AppendASCII("dir2");
+
+  ResourceEntry parent;
+  EXPECT_EQ(FILE_ERROR_OK,
+            metadata_->GetResourceEntryByPath(kPath1.DirName(), &parent));
+
+  ResourceEntry entry1;
+  entry1.set_parent_local_id(parent.local_id());
+  entry1.set_title(kPath1.BaseName().AsUTF8Unsafe());
+  entry1.mutable_file_info()->set_is_directory(true);
+  entry1.set_metadata_edit_state(ResourceEntry::DIRTY);
+  std::string local_id1;
+  EXPECT_EQ(FILE_ERROR_OK, metadata_->AddEntry(entry1, &local_id1));
+
+  ResourceEntry entry2;
+  entry2.set_parent_local_id(local_id1);
+  entry2.set_title(kPath2.BaseName().AsUTF8Unsafe());
+  entry2.mutable_file_info()->set_is_directory(true);
+  entry2.set_metadata_edit_state(ResourceEntry::DIRTY);
+  std::string local_id2;
+  EXPECT_EQ(FILE_ERROR_OK, metadata_->AddEntry(entry2, &local_id2));
+
+  // Start syncing the child first.
+  sync_client_->AddUpdateTask(ClientContext(USER_INITIATED), local_id2);
+  base::RunLoop().RunUntilIdle();
+  // Start syncing the parent later.
+  sync_client_->AddUpdateTask(ClientContext(USER_INITIATED), local_id1);
+  base::RunLoop().RunUntilIdle();
+
+  // Both entries are synced.
+  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(local_id1, &entry1));
+  EXPECT_EQ(ResourceEntry::CLEAN, entry1.metadata_edit_state());
+  EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(local_id2, &entry2));
+  EXPECT_EQ(ResourceEntry::CLEAN, entry2.metadata_edit_state());
+}
+
 }  // namespace internal
 }  // namespace drive