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.
5 #include "chrome/browser/chromeos/drive/file_system/copy_operation.h"
7 #include "base/files/file_util.h"
8 #include "base/task_runner_util.h"
9 #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
10 #include "components/drive/drive_api_util.h"
11 #include "components/drive/file_cache.h"
12 #include "components/drive/file_change.h"
13 #include "components/drive/file_system_core_util.h"
14 #include "components/drive/resource_metadata.h"
15 #include "components/drive/service/fake_drive_service.h"
16 #include "content/public/test/test_utils.h"
17 #include "google_apis/drive/drive_api_parser.h"
18 #include "google_apis/drive/test_util.h"
19 #include "testing/gtest/include/gtest/gtest.h"
22 namespace file_system
{
26 // Used to handle WaitForSyncComplete() calls.
27 bool CopyWaitForSyncCompleteArguments(std::string
* out_local_id
,
28 FileOperationCallback
* out_callback
,
29 const std::string
& local_id
,
30 const FileOperationCallback
& callback
) {
31 *out_local_id
= local_id
;
32 *out_callback
= callback
;
38 class CopyOperationTest
: public OperationTestBase
{
40 void SetUp() override
{
41 OperationTestBase::SetUp();
42 operation_
.reset(new CopyOperation(
43 blocking_task_runner(), delegate(), scheduler(), metadata(), cache()));
46 scoped_ptr
<CopyOperation
> operation_
;
49 TEST_F(CopyOperationTest
, TransferFileFromLocalToRemote_RegularFile
) {
50 const base::FilePath local_src_path
= temp_dir().AppendASCII("local.txt");
51 const base::FilePath
remote_dest_path(
52 FILE_PATH_LITERAL("drive/root/remote.txt"));
54 // Prepare a local file.
56 google_apis::test_util::WriteStringToFile(local_src_path
, "hello"));
57 // Confirm that the remote file does not exist.
59 ASSERT_EQ(FILE_ERROR_NOT_FOUND
,
60 GetLocalResourceEntry(remote_dest_path
, &entry
));
62 // Transfer the local file to Drive.
63 FileError error
= FILE_ERROR_FAILED
;
64 operation_
->TransferFileFromLocalToRemote(
67 google_apis::test_util::CreateCopyResultCallback(&error
));
68 content::RunAllBlockingPoolTasksUntilIdle();
69 EXPECT_EQ(FILE_ERROR_OK
, error
);
71 // TransferFileFromLocalToRemote stores a copy of the local file in the cache,
72 // marks it dirty and requests the observer to upload the file.
73 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(remote_dest_path
, &entry
));
74 EXPECT_EQ(1U, delegate()->updated_local_ids().count(entry
.local_id()));
75 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
76 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_dirty());
78 EXPECT_EQ(1U, delegate()->get_changed_files().size());
79 EXPECT_TRUE(delegate()->get_changed_files().count(remote_dest_path
));
82 TEST_F(CopyOperationTest
, TransferFileFromLocalToRemote_Overwrite
) {
83 const base::FilePath local_src_path
= temp_dir().AppendASCII("local.txt");
84 const base::FilePath
remote_dest_path(
85 FILE_PATH_LITERAL("drive/root/File 1.txt"));
87 // Prepare a local file.
89 google_apis::test_util::WriteStringToFile(local_src_path
, "hello"));
90 // Confirm that the remote file exists.
92 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(remote_dest_path
, &entry
));
94 // Transfer the local file to Drive.
95 FileError error
= FILE_ERROR_FAILED
;
96 operation_
->TransferFileFromLocalToRemote(
99 google_apis::test_util::CreateCopyResultCallback(&error
));
100 content::RunAllBlockingPoolTasksUntilIdle();
101 EXPECT_EQ(FILE_ERROR_OK
, error
);
103 // TransferFileFromLocalToRemote stores a copy of the local file in the cache,
104 // marks it dirty and requests the observer to upload the file.
105 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(remote_dest_path
, &entry
));
106 EXPECT_EQ(1U, delegate()->updated_local_ids().count(entry
.local_id()));
107 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
108 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_dirty());
110 EXPECT_EQ(1U, delegate()->get_changed_files().size());
111 EXPECT_TRUE(delegate()->get_changed_files().count(remote_dest_path
));
114 TEST_F(CopyOperationTest
,
115 TransferFileFromLocalToRemote_ExistingHostedDocument
) {
116 const base::FilePath local_src_path
= temp_dir().AppendASCII("local.gdoc");
117 const base::FilePath
remote_dest_path(FILE_PATH_LITERAL(
118 "drive/root/Directory 1/copied.gdoc"));
120 // Prepare a local file, which is a json file of a hosted document, which
121 // matches "drive/root/Document 1 excludeDir-test".
122 ASSERT_TRUE(util::CreateGDocFile(
124 GURL("https://3_document_self_link/5_document_resource_id"),
125 "5_document_resource_id"));
128 ASSERT_EQ(FILE_ERROR_NOT_FOUND
,
129 GetLocalResourceEntry(remote_dest_path
, &entry
));
131 // Transfer the local file to Drive.
132 FileError error
= FILE_ERROR_FAILED
;
133 operation_
->TransferFileFromLocalToRemote(
136 google_apis::test_util::CreateCopyResultCallback(&error
));
137 content::RunAllBlockingPoolTasksUntilIdle();
138 EXPECT_EQ(FILE_ERROR_OK
, error
);
140 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(remote_dest_path
, &entry
));
142 EXPECT_EQ(1U, delegate()->get_changed_files().size());
143 EXPECT_TRUE(delegate()->get_changed_files().count(remote_dest_path
));
144 // New copy is created.
145 EXPECT_NE("5_document_resource_id", entry
.resource_id());
148 TEST_F(CopyOperationTest
, TransferFileFromLocalToRemote_OrphanHostedDocument
) {
149 const base::FilePath local_src_path
= temp_dir().AppendASCII("local.gdoc");
150 const base::FilePath
remote_dest_path(FILE_PATH_LITERAL(
151 "drive/root/Directory 1/moved.gdoc"));
153 // Prepare a local file, which is a json file of a hosted document, which
154 // matches "drive/other/Orphan Document".
155 ASSERT_TRUE(util::CreateGDocFile(
157 GURL("https://3_document_self_link/orphan_doc_1"),
161 ASSERT_EQ(FILE_ERROR_NOT_FOUND
,
162 GetLocalResourceEntry(remote_dest_path
, &entry
));
164 // Transfer the local file to Drive.
165 FileError error
= FILE_ERROR_FAILED
;
166 operation_
->TransferFileFromLocalToRemote(
169 google_apis::test_util::CreateCopyResultCallback(&error
));
170 content::RunAllBlockingPoolTasksUntilIdle();
171 EXPECT_EQ(FILE_ERROR_OK
, error
);
173 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(remote_dest_path
, &entry
));
174 EXPECT_EQ(ResourceEntry::DIRTY
, entry
.metadata_edit_state());
175 EXPECT_TRUE(delegate()->updated_local_ids().count(entry
.local_id()));
177 EXPECT_EQ(1U, delegate()->get_changed_files().size());
178 EXPECT_TRUE(delegate()->get_changed_files().count(remote_dest_path
));
179 // The original document got new parent.
180 EXPECT_EQ("orphan_doc_1", entry
.resource_id());
183 TEST_F(CopyOperationTest
, TransferFileFromLocalToRemote_NewHostedDocument
) {
184 const base::FilePath local_src_path
= temp_dir().AppendASCII("local.gdoc");
185 const base::FilePath
remote_dest_path(FILE_PATH_LITERAL(
186 "drive/root/Directory 1/moved.gdoc"));
188 // Create a hosted document on the server that is not synced to local yet.
189 google_apis::DriveApiErrorCode gdata_error
= google_apis::DRIVE_OTHER_ERROR
;
190 scoped_ptr
<google_apis::FileResource
> new_gdoc_entry
;
191 fake_service()->AddNewFile(
192 "application/vnd.google-apps.document", "", "", "title", true,
193 google_apis::test_util::CreateCopyResultCallback(&gdata_error
,
195 content::RunAllBlockingPoolTasksUntilIdle();
196 ASSERT_EQ(google_apis::HTTP_CREATED
, gdata_error
);
198 // Prepare a local file, which is a json file of the added hosted document.
199 ASSERT_TRUE(util::CreateGDocFile(
201 GURL("https://3_document_self_link/" + new_gdoc_entry
->file_id()),
202 new_gdoc_entry
->file_id()));
205 ASSERT_EQ(FILE_ERROR_NOT_FOUND
,
206 GetLocalResourceEntry(remote_dest_path
, &entry
));
208 // Transfer the local file to Drive.
209 FileError error
= FILE_ERROR_FAILED
;
210 operation_
->TransferFileFromLocalToRemote(
213 google_apis::test_util::CreateCopyResultCallback(&error
));
214 content::RunAllBlockingPoolTasksUntilIdle();
215 EXPECT_EQ(FILE_ERROR_OK
, error
);
217 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(remote_dest_path
, &entry
));
219 EXPECT_EQ(1U, delegate()->get_changed_files().size());
220 EXPECT_TRUE(delegate()->get_changed_files().count(remote_dest_path
));
221 // The original document got new parent.
222 EXPECT_EQ(new_gdoc_entry
->file_id(), entry
.resource_id());
225 TEST_F(CopyOperationTest
, CopyNotExistingFile
) {
226 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/Dummy file.txt"));
227 base::FilePath
dest_path(FILE_PATH_LITERAL("drive/root/Test.log"));
230 ASSERT_EQ(FILE_ERROR_NOT_FOUND
, GetLocalResourceEntry(src_path
, &entry
));
232 FileError error
= FILE_ERROR_OK
;
233 operation_
->Copy(src_path
,
236 google_apis::test_util::CreateCopyResultCallback(&error
));
237 content::RunAllBlockingPoolTasksUntilIdle();
238 EXPECT_EQ(FILE_ERROR_NOT_FOUND
, error
);
240 EXPECT_EQ(FILE_ERROR_NOT_FOUND
, GetLocalResourceEntry(src_path
, &entry
));
241 EXPECT_EQ(FILE_ERROR_NOT_FOUND
, GetLocalResourceEntry(dest_path
, &entry
));
242 EXPECT_TRUE(delegate()->get_changed_files().empty());
245 TEST_F(CopyOperationTest
, CopyFileToNonExistingDirectory
) {
246 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
247 base::FilePath
dest_path(FILE_PATH_LITERAL("drive/root/Dummy/Test.log"));
250 ASSERT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
251 ASSERT_EQ(FILE_ERROR_NOT_FOUND
,
252 GetLocalResourceEntry(dest_path
.DirName(), &entry
));
254 FileError error
= FILE_ERROR_OK
;
255 operation_
->Copy(src_path
,
258 google_apis::test_util::CreateCopyResultCallback(&error
));
259 content::RunAllBlockingPoolTasksUntilIdle();
260 EXPECT_EQ(FILE_ERROR_NOT_FOUND
, error
);
262 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
263 EXPECT_EQ(FILE_ERROR_NOT_FOUND
, GetLocalResourceEntry(dest_path
, &entry
));
264 EXPECT_TRUE(delegate()->get_changed_files().empty());
267 // Test the case where the parent of the destination path is an existing file,
269 TEST_F(CopyOperationTest
, CopyFileToInvalidPath
) {
270 base::FilePath
src_path(FILE_PATH_LITERAL(
271 "drive/root/Document 1 excludeDir-test.gdoc"));
272 base::FilePath
dest_path(FILE_PATH_LITERAL(
273 "drive/root/Duplicate Name.txt/Document 1 excludeDir-test.gdoc"));
276 ASSERT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
277 ASSERT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
.DirName(), &entry
));
278 ASSERT_FALSE(entry
.file_info().is_directory());
280 FileError error
= FILE_ERROR_OK
;
281 operation_
->Copy(src_path
,
284 google_apis::test_util::CreateCopyResultCallback(&error
));
285 content::RunAllBlockingPoolTasksUntilIdle();
286 EXPECT_EQ(FILE_ERROR_NOT_A_DIRECTORY
, error
);
288 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
289 EXPECT_EQ(FILE_ERROR_NOT_FOUND
, GetLocalResourceEntry(dest_path
, &entry
));
290 EXPECT_TRUE(delegate()->get_changed_files().empty());
293 TEST_F(CopyOperationTest
, CopyDirtyFile
) {
294 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
295 base::FilePath
dest_path(FILE_PATH_LITERAL(
296 "drive/root/Directory 1/New File.txt"));
298 ResourceEntry src_entry
;
299 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &src_entry
));
301 // Store a dirty cache file.
302 base::FilePath temp_file
;
303 EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file
));
304 std::string contents
= "test content";
305 EXPECT_TRUE(google_apis::test_util::WriteStringToFile(temp_file
, contents
));
306 FileError error
= FILE_ERROR_FAILED
;
307 base::PostTaskAndReplyWithResult(
308 blocking_task_runner(),
310 base::Bind(&internal::FileCache::Store
,
311 base::Unretained(cache()),
312 src_entry
.local_id(),
315 internal::FileCache::FILE_OPERATION_MOVE
),
316 google_apis::test_util::CreateCopyResultCallback(&error
));
317 content::RunAllBlockingPoolTasksUntilIdle();
318 EXPECT_EQ(FILE_ERROR_OK
, error
);
321 operation_
->Copy(src_path
,
324 google_apis::test_util::CreateCopyResultCallback(&error
));
325 content::RunAllBlockingPoolTasksUntilIdle();
326 EXPECT_EQ(FILE_ERROR_OK
, error
);
328 ResourceEntry dest_entry
;
329 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
, &dest_entry
));
330 EXPECT_EQ(ResourceEntry::DIRTY
, dest_entry
.metadata_edit_state());
332 EXPECT_EQ(1u, delegate()->updated_local_ids().size());
333 EXPECT_TRUE(delegate()->updated_local_ids().count(dest_entry
.local_id()));
334 EXPECT_EQ(1u, delegate()->get_changed_files().size());
335 EXPECT_TRUE(delegate()->get_changed_files().count(dest_path
));
337 // Copied cache file should be dirty.
338 EXPECT_TRUE(dest_entry
.file_specific_info().cache_state().is_dirty());
340 // File contents should match.
341 base::FilePath cache_file_path
;
342 base::PostTaskAndReplyWithResult(
343 blocking_task_runner(),
345 base::Bind(&internal::FileCache::GetFile
,
346 base::Unretained(cache()),
347 dest_entry
.local_id(),
349 google_apis::test_util::CreateCopyResultCallback(&error
));
350 content::RunAllBlockingPoolTasksUntilIdle();
351 EXPECT_EQ(FILE_ERROR_OK
, error
);
353 std::string copied_contents
;
354 EXPECT_TRUE(base::ReadFileToString(cache_file_path
, &copied_contents
));
355 EXPECT_EQ(contents
, copied_contents
);
358 TEST_F(CopyOperationTest
, CopyFileOverwriteFile
) {
359 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
360 base::FilePath
dest_path(FILE_PATH_LITERAL(
361 "drive/root/Directory 1/SubDirectory File 1.txt"));
363 ResourceEntry old_dest_entry
;
364 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
, &old_dest_entry
));
366 FileError error
= FILE_ERROR_OK
;
367 operation_
->Copy(src_path
,
370 google_apis::test_util::CreateCopyResultCallback(&error
));
371 content::RunAllBlockingPoolTasksUntilIdle();
372 EXPECT_EQ(FILE_ERROR_OK
, error
);
374 ResourceEntry new_dest_entry
;
375 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
, &new_dest_entry
));
377 EXPECT_EQ(1u, delegate()->updated_local_ids().size());
378 EXPECT_TRUE(delegate()->updated_local_ids().count(old_dest_entry
.local_id()));
379 EXPECT_EQ(1u, delegate()->get_changed_files().size());
380 EXPECT_TRUE(delegate()->get_changed_files().count(dest_path
));
383 TEST_F(CopyOperationTest
, CopyFileOverwriteDirectory
) {
384 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
385 base::FilePath
dest_path(FILE_PATH_LITERAL("drive/root/Directory 1"));
387 FileError error
= FILE_ERROR_OK
;
388 operation_
->Copy(src_path
,
391 google_apis::test_util::CreateCopyResultCallback(&error
));
392 content::RunAllBlockingPoolTasksUntilIdle();
393 EXPECT_EQ(FILE_ERROR_INVALID_OPERATION
, error
);
396 TEST_F(CopyOperationTest
, CopyDirectory
) {
397 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/Directory 1"));
398 base::FilePath
dest_path(FILE_PATH_LITERAL("drive/root/New Directory"));
401 ASSERT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
402 ASSERT_TRUE(entry
.file_info().is_directory());
403 ASSERT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
.DirName(), &entry
));
404 ASSERT_TRUE(entry
.file_info().is_directory());
406 FileError error
= FILE_ERROR_OK
;
407 operation_
->Copy(src_path
,
410 google_apis::test_util::CreateCopyResultCallback(&error
));
411 content::RunAllBlockingPoolTasksUntilIdle();
412 EXPECT_EQ(FILE_ERROR_NOT_A_FILE
, error
);
415 TEST_F(CopyOperationTest
, PreserveLastModified
) {
416 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
417 base::FilePath
dest_path(FILE_PATH_LITERAL("drive/root/File 2.txt"));
420 ASSERT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
421 ASSERT_EQ(FILE_ERROR_OK
,
422 GetLocalResourceEntry(dest_path
.DirName(), &entry
));
424 FileError error
= FILE_ERROR_OK
;
425 operation_
->Copy(src_path
,
427 true, // Preserve last modified.
428 google_apis::test_util::CreateCopyResultCallback(&error
));
429 content::RunAllBlockingPoolTasksUntilIdle();
430 EXPECT_EQ(FILE_ERROR_OK
, error
);
432 ResourceEntry entry2
;
433 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(src_path
, &entry
));
434 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
, &entry2
));
435 EXPECT_EQ(entry
.file_info().last_modified(),
436 entry2
.file_info().last_modified());
439 TEST_F(CopyOperationTest
, WaitForSyncComplete
) {
440 // Create a directory locally.
441 base::FilePath
src_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
442 base::FilePath
directory_path(FILE_PATH_LITERAL("drive/root/New Directory"));
443 base::FilePath dest_path
= directory_path
.AppendASCII("File 1.txt");
445 ResourceEntry directory_parent
;
446 EXPECT_EQ(FILE_ERROR_OK
,
447 GetLocalResourceEntry(directory_path
.DirName(), &directory_parent
));
449 ResourceEntry directory
;
450 directory
.set_parent_local_id(directory_parent
.local_id());
451 directory
.set_title(directory_path
.BaseName().AsUTF8Unsafe());
452 directory
.mutable_file_info()->set_is_directory(true);
453 directory
.set_metadata_edit_state(ResourceEntry::DIRTY
);
455 std::string directory_local_id
;
456 FileError error
= FILE_ERROR_FAILED
;
457 base::PostTaskAndReplyWithResult(
458 blocking_task_runner(),
460 base::Bind(&internal::ResourceMetadata::AddEntry
,
461 base::Unretained(metadata()), directory
, &directory_local_id
),
462 google_apis::test_util::CreateCopyResultCallback(&error
));
463 content::RunAllBlockingPoolTasksUntilIdle();
464 EXPECT_EQ(FILE_ERROR_OK
, error
);
466 // Try to copy a file to the new directory which lacks resource ID.
467 // This should result in waiting for the directory to sync.
468 std::string waited_local_id
;
469 FileOperationCallback pending_callback
;
470 delegate()->set_wait_for_sync_complete_handler(
471 base::Bind(&CopyWaitForSyncCompleteArguments
,
472 &waited_local_id
, &pending_callback
));
474 FileError copy_error
= FILE_ERROR_FAILED
;
475 operation_
->Copy(src_path
,
477 true, // Preserve last modified.
478 google_apis::test_util::CreateCopyResultCallback(
480 content::RunAllBlockingPoolTasksUntilIdle();
481 EXPECT_EQ(directory_local_id
, waited_local_id
);
482 ASSERT_FALSE(pending_callback
.is_null());
484 // Add a new directory to the server and store the resource ID locally.
485 google_apis::DriveApiErrorCode status
= google_apis::DRIVE_OTHER_ERROR
;
486 scoped_ptr
<google_apis::FileResource
> file_resource
;
487 fake_service()->AddNewDirectory(
488 directory_parent
.resource_id(), directory
.title(),
489 AddNewDirectoryOptions(),
490 google_apis::test_util::CreateCopyResultCallback(&status
,
492 content::RunAllBlockingPoolTasksUntilIdle();
493 EXPECT_EQ(google_apis::HTTP_CREATED
, status
);
494 ASSERT_TRUE(file_resource
);
496 directory
.set_local_id(directory_local_id
);
497 directory
.set_resource_id(file_resource
->file_id());
498 base::PostTaskAndReplyWithResult(
499 blocking_task_runner(),
501 base::Bind(&internal::ResourceMetadata::RefreshEntry
,
502 base::Unretained(metadata()), directory
),
503 google_apis::test_util::CreateCopyResultCallback(&error
));
504 content::RunAllBlockingPoolTasksUntilIdle();
505 EXPECT_EQ(FILE_ERROR_OK
, error
);
507 // Resume the copy operation.
508 pending_callback
.Run(FILE_ERROR_OK
);
509 content::RunAllBlockingPoolTasksUntilIdle();
511 EXPECT_EQ(FILE_ERROR_OK
, copy_error
);
513 EXPECT_EQ(FILE_ERROR_OK
, GetLocalResourceEntry(dest_path
, &entry
));
516 } // namespace file_system