1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/drive/sync/entry_update_performer.h"
6
7#include "base/callback_helpers.h"
8#include "base/files/file_util.h"
9#include "base/md5.h"
10#include "base/task_runner_util.h"
11#include "chrome/browser/chromeos/drive/file_cache.h"
12#include "chrome/browser/chromeos/drive/file_system/download_operation.h"
13#include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
14#include "chrome/browser/chromeos/drive/job_scheduler.h"
15#include "chrome/browser/chromeos/drive/resource_metadata.h"
16#include "chrome/browser/drive/drive_api_util.h"
17#include "chrome/browser/drive/fake_drive_service.h"
18#include "content/public/test/test_utils.h"
19#include "google_apis/drive/drive_api_parser.h"
20#include "google_apis/drive/test_util.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace drive {
24namespace internal {
25
26class EntryUpdatePerformerTest : public file_system::OperationTestBase {
27 protected:
28  virtual void SetUp() OVERRIDE {
29    OperationTestBase::SetUp();
30    performer_.reset(new EntryUpdatePerformer(blocking_task_runner(),
31                                              delegate(),
32                                              scheduler(),
33                                              metadata(),
34                                              cache(),
35                                              loader_controller()));
36  }
37
38  // Stores |content| to the cache and mark it as dirty.
39  FileError StoreAndMarkDirty(const std::string& local_id,
40                              const std::string& content) {
41    base::FilePath path;
42    if (!base::CreateTemporaryFileInDir(temp_dir(), &path) ||
43        !google_apis::test_util::WriteStringToFile(path, content))
44      return FILE_ERROR_FAILED;
45
46    // Store the file to cache.
47    FileError error = FILE_ERROR_FAILED;
48    base::PostTaskAndReplyWithResult(
49        blocking_task_runner(),
50        FROM_HERE,
51        base::Bind(&FileCache::Store,
52                   base::Unretained(cache()),
53                   local_id, std::string(), path,
54                   FileCache::FILE_OPERATION_COPY),
55        google_apis::test_util::CreateCopyResultCallback(&error));
56    content::RunAllBlockingPoolTasksUntilIdle();
57    return error;
58  }
59
60  scoped_ptr<EntryUpdatePerformer> performer_;
61};
62
63TEST_F(EntryUpdatePerformerTest, UpdateEntry) {
64  base::FilePath src_path(
65      FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
66  base::FilePath dest_path(
67      FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder"));
68
69  ResourceEntry src_entry, dest_entry;
70  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(src_path, &src_entry));
71  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(dest_path, &dest_entry));
72
73  // Update local entry.
74  base::Time new_last_modified = base::Time::FromInternalValue(
75      src_entry.file_info().last_modified()) + base::TimeDelta::FromSeconds(1);
76  base::Time new_last_accessed = base::Time::FromInternalValue(
77      src_entry.file_info().last_accessed()) + base::TimeDelta::FromSeconds(2);
78
79  src_entry.set_parent_local_id(dest_entry.local_id());
80  src_entry.set_title("Moved" + src_entry.title());
81  src_entry.mutable_file_info()->set_last_modified(
82      new_last_modified.ToInternalValue());
83  src_entry.mutable_file_info()->set_last_accessed(
84      new_last_accessed.ToInternalValue());
85  src_entry.set_metadata_edit_state(ResourceEntry::DIRTY);
86
87  FileError error = FILE_ERROR_FAILED;
88  base::PostTaskAndReplyWithResult(
89      blocking_task_runner(),
90      FROM_HERE,
91      base::Bind(&ResourceMetadata::RefreshEntry,
92                 base::Unretained(metadata()),
93                 src_entry),
94      google_apis::test_util::CreateCopyResultCallback(&error));
95  content::RunAllBlockingPoolTasksUntilIdle();
96  EXPECT_EQ(FILE_ERROR_OK, error);
97
98  // Perform server side update.
99  error = FILE_ERROR_FAILED;
100  performer_->UpdateEntry(
101      src_entry.local_id(),
102      ClientContext(USER_INITIATED),
103      google_apis::test_util::CreateCopyResultCallback(&error));
104  content::RunAllBlockingPoolTasksUntilIdle();
105  EXPECT_EQ(FILE_ERROR_OK, error);
106
107  // Verify the file is updated on the server.
108  google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
109  scoped_ptr<google_apis::FileResource> gdata_entry;
110  fake_service()->GetFileResource(
111      src_entry.resource_id(),
112      google_apis::test_util::CreateCopyResultCallback(&gdata_error,
113                                                       &gdata_entry));
114  content::RunAllBlockingPoolTasksUntilIdle();
115  EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
116  ASSERT_TRUE(gdata_entry);
117
118  EXPECT_EQ(src_entry.title(), gdata_entry->title());
119  EXPECT_EQ(new_last_modified, gdata_entry->modified_date());
120  EXPECT_EQ(new_last_accessed, gdata_entry->last_viewed_by_me_date());
121
122  ASSERT_FALSE(gdata_entry->parents().empty());
123  EXPECT_EQ(dest_entry.resource_id(), gdata_entry->parents()[0].file_id());
124}
125
126// Tests updating metadata of a file with a non-dirty cache file.
127TEST_F(EntryUpdatePerformerTest, UpdateEntry_WithNonDirtyCache) {
128  base::FilePath src_path(
129      FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
130
131  // Download the file content to prepare a non-dirty cache file.
132  file_system::DownloadOperation download_operation(
133      blocking_task_runner(), delegate(), scheduler(), metadata(), cache(),
134      temp_dir());
135  FileError error = FILE_ERROR_FAILED;
136  base::FilePath cache_file_path;
137  scoped_ptr<ResourceEntry> src_entry;
138  download_operation.EnsureFileDownloadedByPath(
139      src_path,
140      ClientContext(USER_INITIATED),
141      GetFileContentInitializedCallback(),
142      google_apis::GetContentCallback(),
143      google_apis::test_util::CreateCopyResultCallback(
144          &error, &cache_file_path, &src_entry));
145  content::RunAllBlockingPoolTasksUntilIdle();
146  EXPECT_EQ(FILE_ERROR_OK, error);
147  ASSERT_TRUE(src_entry);
148
149  // Update the entry locally.
150  src_entry->set_title("Updated" + src_entry->title());
151  src_entry->set_metadata_edit_state(ResourceEntry::DIRTY);
152
153  error = FILE_ERROR_FAILED;
154  base::PostTaskAndReplyWithResult(
155      blocking_task_runner(),
156      FROM_HERE,
157      base::Bind(&ResourceMetadata::RefreshEntry,
158                 base::Unretained(metadata()),
159                 *src_entry),
160      google_apis::test_util::CreateCopyResultCallback(&error));
161  content::RunAllBlockingPoolTasksUntilIdle();
162  EXPECT_EQ(FILE_ERROR_OK, error);
163
164  // Perform server side update. This shouldn't fail. (crbug.com/358590)
165  error = FILE_ERROR_FAILED;
166  performer_->UpdateEntry(
167      src_entry->local_id(),
168      ClientContext(USER_INITIATED),
169      google_apis::test_util::CreateCopyResultCallback(&error));
170  content::RunAllBlockingPoolTasksUntilIdle();
171  EXPECT_EQ(FILE_ERROR_OK, error);
172
173  // Verify the file is updated on the server.
174  google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
175  scoped_ptr<google_apis::FileResource> gdata_entry;
176  fake_service()->GetFileResource(
177      src_entry->resource_id(),
178      google_apis::test_util::CreateCopyResultCallback(&gdata_error,
179                                                       &gdata_entry));
180  content::RunAllBlockingPoolTasksUntilIdle();
181  EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
182  ASSERT_TRUE(gdata_entry);
183  EXPECT_EQ(src_entry->title(), gdata_entry->title());
184}
185
186TEST_F(EntryUpdatePerformerTest, UpdateEntry_NotFound) {
187  const std::string id = "this ID should result in NOT_FOUND";
188  FileError error = FILE_ERROR_FAILED;
189  performer_->UpdateEntry(
190      id, ClientContext(USER_INITIATED),
191      google_apis::test_util::CreateCopyResultCallback(&error));
192  content::RunAllBlockingPoolTasksUntilIdle();
193  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
194}
195
196TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdate) {
197  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
198  const std::string kResourceId("2_file_resource_id");
199
200  const std::string local_id = GetLocalId(kFilePath);
201  EXPECT_FALSE(local_id.empty());
202
203  const std::string kTestFileContent = "I'm being uploaded! Yay!";
204  EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
205
206  int64 original_changestamp =
207      fake_service()->about_resource().largest_change_id();
208
209  // The callback will be called upon completion of UpdateEntry().
210  FileError error = FILE_ERROR_FAILED;
211  performer_->UpdateEntry(
212      local_id,
213      ClientContext(USER_INITIATED),
214      google_apis::test_util::CreateCopyResultCallback(&error));
215  content::RunAllBlockingPoolTasksUntilIdle();
216  EXPECT_EQ(FILE_ERROR_OK, error);
217
218  // Check that the server has received an update.
219  EXPECT_LT(original_changestamp,
220            fake_service()->about_resource().largest_change_id());
221
222  // Check that the file size is updated to that of the updated content.
223  google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
224  scoped_ptr<google_apis::FileResource> server_entry;
225  fake_service()->GetFileResource(
226      kResourceId,
227      google_apis::test_util::CreateCopyResultCallback(&gdata_error,
228                                                       &server_entry));
229  content::RunAllBlockingPoolTasksUntilIdle();
230  EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
231  EXPECT_EQ(static_cast<int64>(kTestFileContent.size()),
232            server_entry->file_size());
233
234  // Make sure that the cache is no longer dirty.
235  ResourceEntry entry;
236  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
237  EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
238}
239
240TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdateMd5Check) {
241  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
242  const std::string kResourceId("2_file_resource_id");
243
244  const std::string local_id = GetLocalId(kFilePath);
245  EXPECT_FALSE(local_id.empty());
246
247  const std::string kTestFileContent = "I'm being uploaded! Yay!";
248  EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
249
250  int64 original_changestamp =
251      fake_service()->about_resource().largest_change_id();
252
253  // The callback will be called upon completion of UpdateEntry().
254  FileError error = FILE_ERROR_FAILED;
255  performer_->UpdateEntry(
256      local_id,
257      ClientContext(USER_INITIATED),
258      google_apis::test_util::CreateCopyResultCallback(&error));
259  content::RunAllBlockingPoolTasksUntilIdle();
260  EXPECT_EQ(FILE_ERROR_OK, error);
261
262  // Check that the server has received an update.
263  EXPECT_LT(original_changestamp,
264            fake_service()->about_resource().largest_change_id());
265
266  // Check that the file size is updated to that of the updated content.
267  google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
268  scoped_ptr<google_apis::FileResource> server_entry;
269  fake_service()->GetFileResource(
270      kResourceId,
271      google_apis::test_util::CreateCopyResultCallback(&gdata_error,
272                                                       &server_entry));
273  content::RunAllBlockingPoolTasksUntilIdle();
274  EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
275  EXPECT_EQ(static_cast<int64>(kTestFileContent.size()),
276            server_entry->file_size());
277
278  // Make sure that the cache is no longer dirty.
279  ResourceEntry entry;
280  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
281  EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
282
283  // Again mark the cache file dirty.
284  scoped_ptr<base::ScopedClosureRunner> file_closer;
285  error = FILE_ERROR_FAILED;
286  base::PostTaskAndReplyWithResult(
287      blocking_task_runner(),
288      FROM_HERE,
289      base::Bind(&FileCache::OpenForWrite,
290                 base::Unretained(cache()),
291                 local_id,
292                 &file_closer),
293      google_apis::test_util::CreateCopyResultCallback(&error));
294  content::RunAllBlockingPoolTasksUntilIdle();
295  EXPECT_EQ(FILE_ERROR_OK, error);
296  file_closer.reset();
297
298  // And call UpdateEntry again.
299  // In this case, although the file is marked as dirty, but the content
300  // hasn't been changed. Thus, the actual uploading should be skipped.
301  original_changestamp = fake_service()->about_resource().largest_change_id();
302  error = FILE_ERROR_FAILED;
303  performer_->UpdateEntry(
304      local_id,
305      ClientContext(USER_INITIATED),
306      google_apis::test_util::CreateCopyResultCallback(&error));
307  content::RunAllBlockingPoolTasksUntilIdle();
308  EXPECT_EQ(FILE_ERROR_OK, error);
309
310  EXPECT_EQ(original_changestamp,
311            fake_service()->about_resource().largest_change_id());
312
313  // Make sure that the cache is no longer dirty.
314  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
315  EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
316}
317
318TEST_F(EntryUpdatePerformerTest, UpdateEntry_OpenedForWrite) {
319  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
320  const std::string kResourceId("2_file_resource_id");
321
322  const std::string local_id = GetLocalId(kFilePath);
323  EXPECT_FALSE(local_id.empty());
324
325  const std::string kTestFileContent = "I'm being uploaded! Yay!";
326  EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
327
328  // Emulate a situation where someone is writing to the file.
329  scoped_ptr<base::ScopedClosureRunner> file_closer;
330  FileError error = FILE_ERROR_FAILED;
331  base::PostTaskAndReplyWithResult(
332      blocking_task_runner(),
333      FROM_HERE,
334      base::Bind(&FileCache::OpenForWrite,
335                 base::Unretained(cache()),
336                 local_id,
337                 &file_closer),
338      google_apis::test_util::CreateCopyResultCallback(&error));
339  content::RunAllBlockingPoolTasksUntilIdle();
340  EXPECT_EQ(FILE_ERROR_OK, error);
341
342  // Update. This should not clear the dirty bit.
343  error = FILE_ERROR_FAILED;
344  performer_->UpdateEntry(
345      local_id,
346      ClientContext(USER_INITIATED),
347      google_apis::test_util::CreateCopyResultCallback(&error));
348  content::RunAllBlockingPoolTasksUntilIdle();
349  EXPECT_EQ(FILE_ERROR_OK, error);
350
351  // Make sure that the cache is still dirty.
352  ResourceEntry entry;
353  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
354  EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty());
355
356  // Close the file.
357  file_closer.reset();
358
359  // Update. This should clear the dirty bit.
360  error = FILE_ERROR_FAILED;
361  performer_->UpdateEntry(
362      local_id,
363      ClientContext(USER_INITIATED),
364      google_apis::test_util::CreateCopyResultCallback(&error));
365  content::RunAllBlockingPoolTasksUntilIdle();
366  EXPECT_EQ(FILE_ERROR_OK, error);
367
368  // Make sure that the cache is no longer dirty.
369  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
370  EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
371}
372
373TEST_F(EntryUpdatePerformerTest, UpdateEntry_UploadNewFile) {
374  // Create a new file locally.
375  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt"));
376
377  ResourceEntry parent;
378  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent));
379
380  ResourceEntry entry;
381  entry.set_parent_local_id(parent.local_id());
382  entry.set_title(kFilePath.BaseName().AsUTF8Unsafe());
383  entry.mutable_file_specific_info()->set_content_mime_type("text/plain");
384  entry.set_metadata_edit_state(ResourceEntry::DIRTY);
385
386  FileError error = FILE_ERROR_FAILED;
387  std::string local_id;
388  base::PostTaskAndReplyWithResult(
389      blocking_task_runner(),
390      FROM_HERE,
391      base::Bind(&internal::ResourceMetadata::AddEntry,
392                 base::Unretained(metadata()),
393                 entry,
394                 &local_id),
395      google_apis::test_util::CreateCopyResultCallback(&error));
396  content::RunAllBlockingPoolTasksUntilIdle();
397  EXPECT_EQ(FILE_ERROR_OK, error);
398
399  // Update. This should result in creating a new file on the server.
400  error = FILE_ERROR_FAILED;
401  performer_->UpdateEntry(
402      local_id,
403      ClientContext(USER_INITIATED),
404      google_apis::test_util::CreateCopyResultCallback(&error));
405  content::RunAllBlockingPoolTasksUntilIdle();
406  EXPECT_EQ(FILE_ERROR_OK, error);
407
408  // The entry got a resource ID.
409  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
410  EXPECT_FALSE(entry.resource_id().empty());
411  EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state());
412
413  // Make sure that the cache is no longer dirty.
414  EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
415
416  // Make sure that we really created a file.
417  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
418  scoped_ptr<google_apis::FileResource> server_entry;
419  fake_service()->GetFileResource(
420      entry.resource_id(),
421      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
422  content::RunAllBlockingPoolTasksUntilIdle();
423  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
424  ASSERT_TRUE(server_entry);
425  EXPECT_FALSE(server_entry->IsDirectory());
426}
427
428TEST_F(EntryUpdatePerformerTest, UpdateEntry_NewFileOpendForWrite) {
429  // Create a new file locally.
430  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt"));
431
432  ResourceEntry parent;
433  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent));
434
435  ResourceEntry entry;
436  entry.set_parent_local_id(parent.local_id());
437  entry.set_title(kFilePath.BaseName().AsUTF8Unsafe());
438  entry.mutable_file_specific_info()->set_content_mime_type("text/plain");
439  entry.set_metadata_edit_state(ResourceEntry::DIRTY);
440
441  FileError error = FILE_ERROR_FAILED;
442  std::string local_id;
443  base::PostTaskAndReplyWithResult(
444      blocking_task_runner(),
445      FROM_HERE,
446      base::Bind(&internal::ResourceMetadata::AddEntry,
447                 base::Unretained(metadata()),
448                 entry,
449                 &local_id),
450      google_apis::test_util::CreateCopyResultCallback(&error));
451  content::RunAllBlockingPoolTasksUntilIdle();
452  EXPECT_EQ(FILE_ERROR_OK, error);
453
454  const std::string kTestFileContent = "This is a new file.";
455  EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
456
457  // Emulate a situation where someone is writing to the file.
458  scoped_ptr<base::ScopedClosureRunner> file_closer;
459  error = FILE_ERROR_FAILED;
460  base::PostTaskAndReplyWithResult(
461      blocking_task_runner(),
462      FROM_HERE,
463      base::Bind(&FileCache::OpenForWrite,
464                 base::Unretained(cache()),
465                 local_id,
466                 &file_closer),
467      google_apis::test_util::CreateCopyResultCallback(&error));
468  content::RunAllBlockingPoolTasksUntilIdle();
469  EXPECT_EQ(FILE_ERROR_OK, error);
470
471  // Update, but no update is performed because the file is opened.
472  error = FILE_ERROR_FAILED;
473  performer_->UpdateEntry(
474      local_id,
475      ClientContext(USER_INITIATED),
476      google_apis::test_util::CreateCopyResultCallback(&error));
477  content::RunAllBlockingPoolTasksUntilIdle();
478  EXPECT_EQ(FILE_ERROR_OK, error);
479
480  // The entry hasn't got a resource ID yet.
481  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
482  EXPECT_TRUE(entry.resource_id().empty());
483
484  // Close the file.
485  file_closer.reset();
486
487  // Update. This should result in creating a new file on the server.
488  error = FILE_ERROR_FAILED;
489  performer_->UpdateEntry(
490      local_id,
491      ClientContext(USER_INITIATED),
492      google_apis::test_util::CreateCopyResultCallback(&error));
493  content::RunAllBlockingPoolTasksUntilIdle();
494  EXPECT_EQ(FILE_ERROR_OK, error);
495
496  // The entry got a resource ID.
497  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
498  EXPECT_FALSE(entry.resource_id().empty());
499  EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state());
500}
501
502TEST_F(EntryUpdatePerformerTest, UpdateEntry_CreateDirectory) {
503  // Create a new directory locally.
504  const base::FilePath kPath(FILE_PATH_LITERAL("drive/root/New Directory"));
505
506  ResourceEntry parent;
507  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath.DirName(), &parent));
508
509  ResourceEntry entry;
510  entry.set_parent_local_id(parent.local_id());
511  entry.set_title(kPath.BaseName().AsUTF8Unsafe());
512  entry.mutable_file_info()->set_is_directory(true);
513  entry.set_metadata_edit_state(ResourceEntry::DIRTY);
514
515  FileError error = FILE_ERROR_FAILED;
516  std::string local_id;
517  base::PostTaskAndReplyWithResult(
518      blocking_task_runner(),
519      FROM_HERE,
520      base::Bind(&internal::ResourceMetadata::AddEntry,
521                 base::Unretained(metadata()),
522                 entry,
523                 &local_id),
524      google_apis::test_util::CreateCopyResultCallback(&error));
525  content::RunAllBlockingPoolTasksUntilIdle();
526  EXPECT_EQ(FILE_ERROR_OK, error);
527
528  // Update. This should result in creating a new directory on the server.
529  error = FILE_ERROR_FAILED;
530  performer_->UpdateEntry(
531      local_id,
532      ClientContext(USER_INITIATED),
533      google_apis::test_util::CreateCopyResultCallback(&error));
534  content::RunAllBlockingPoolTasksUntilIdle();
535  EXPECT_EQ(FILE_ERROR_OK, error);
536
537  // The entry got a resource ID.
538  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath, &entry));
539  EXPECT_FALSE(entry.resource_id().empty());
540  EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state());
541
542  // Make sure that we really created a directory.
543  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
544  scoped_ptr<google_apis::FileResource> server_entry;
545  fake_service()->GetFileResource(
546      entry.resource_id(),
547      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
548  content::RunAllBlockingPoolTasksUntilIdle();
549  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
550  ASSERT_TRUE(server_entry);
551  EXPECT_TRUE(server_entry->IsDirectory());
552}
553
554TEST_F(EntryUpdatePerformerTest, UpdateEntry_InsufficientPermission) {
555  base::FilePath src_path(
556      FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
557
558  ResourceEntry src_entry;
559  EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(src_path, &src_entry));
560
561  // Update local entry.
562  ResourceEntry updated_entry(src_entry);
563  updated_entry.set_title("Moved" + src_entry.title());
564  updated_entry.set_metadata_edit_state(ResourceEntry::DIRTY);
565
566  FileError error = FILE_ERROR_FAILED;
567  base::PostTaskAndReplyWithResult(
568      blocking_task_runner(),
569      FROM_HERE,
570      base::Bind(&ResourceMetadata::RefreshEntry,
571                 base::Unretained(metadata()),
572                 updated_entry),
573      google_apis::test_util::CreateCopyResultCallback(&error));
574  content::RunAllBlockingPoolTasksUntilIdle();
575  EXPECT_EQ(FILE_ERROR_OK, error);
576
577  // Set user permission to forbid server side update.
578  EXPECT_EQ(google_apis::HTTP_SUCCESS, fake_service()->SetUserPermission(
579      src_entry.resource_id(), google_apis::drive::PERMISSION_ROLE_READER));
580
581  // Try to perform update.
582  error = FILE_ERROR_FAILED;
583  performer_->UpdateEntry(
584      src_entry.local_id(),
585      ClientContext(USER_INITIATED),
586      google_apis::test_util::CreateCopyResultCallback(&error));
587  content::RunAllBlockingPoolTasksUntilIdle();
588  EXPECT_EQ(FILE_ERROR_OK, error);
589
590  // This should result in reverting the local change.
591  ResourceEntry result_entry;
592  EXPECT_EQ(FILE_ERROR_OK,
593            GetLocalResourceEntryById(src_entry.local_id(), &result_entry));
594  EXPECT_EQ(src_entry.title(), result_entry.title());
595}
596
597}  // namespace internal
598}  // namespace drive
599