local_file_change_tracker_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/sync_file_system/local/local_file_change_tracker.h"
6
7#include <deque>
8#include <set>
9
10#include "base/basictypes.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/message_loop/message_loop.h"
14#include "base/message_loop/message_loop_proxy.h"
15#include "base/stl_util.h"
16#include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
17#include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
18#include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
19#include "chrome/browser/sync_file_system/sync_status_code.h"
20#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "webkit/browser/blob/mock_blob_url_request_context.h"
23#include "webkit/browser/fileapi/file_system_context.h"
24#include "webkit/browser/quota/quota_manager.h"
25
26using fileapi::FileSystemContext;
27using fileapi::FileSystemURL;
28using fileapi::FileSystemURLSet;
29using webkit_blob::MockBlobURLRequestContext;
30using webkit_blob::ScopedTextBlob;
31
32namespace sync_file_system {
33
34class LocalFileChangeTrackerTest : public testing::Test {
35 public:
36  LocalFileChangeTrackerTest()
37      : message_loop_(base::MessageLoop::TYPE_IO),
38        file_system_(GURL("http://example.com"),
39                     base::MessageLoopProxy::current().get(),
40                     base::MessageLoopProxy::current().get()) {}
41
42  virtual void SetUp() OVERRIDE {
43    file_system_.SetUp();
44
45    sync_context_ =
46        new LocalFileSyncContext(base::FilePath(),
47                                 base::MessageLoopProxy::current().get(),
48                                 base::MessageLoopProxy::current().get());
49    ASSERT_EQ(
50        sync_file_system::SYNC_STATUS_OK,
51        file_system_.MaybeInitializeFileSystemContext(sync_context_.get()));
52  }
53
54  virtual void TearDown() OVERRIDE {
55    if (sync_context_.get())
56      sync_context_->ShutdownOnUIThread();
57    sync_context_ = NULL;
58
59    message_loop_.RunUntilIdle();
60    file_system_.TearDown();
61    // Make sure we don't leave the external filesystem.
62    // (CannedSyncableFileSystem::TearDown does not do this as there may be
63    // multiple syncable file systems registered for the name)
64    RevokeSyncableFileSystem();
65  }
66
67 protected:
68  FileSystemURL URL(const std::string& path) {
69    return file_system_.URL(path);
70  }
71
72  FileSystemContext* file_system_context() {
73    return file_system_.file_system_context();
74  }
75
76  LocalFileChangeTracker* change_tracker() {
77    return file_system_.backend()->change_tracker();
78  }
79
80  void VerifyAndClearChange(const FileSystemURL& url,
81                            const FileChange& expected_change) {
82    SCOPED_TRACE(testing::Message() << url.DebugString() <<
83                 " expecting:" << expected_change.DebugString());
84    // Get the changes for URL and verify.
85    FileChangeList changes;
86    change_tracker()->GetChangesForURL(url, &changes);
87    ASSERT_EQ(1U, changes.size());
88    SCOPED_TRACE(testing::Message() << url.DebugString() <<
89                 " actual:" << changes.DebugString());
90    EXPECT_EQ(expected_change, changes.list()[0]);
91
92    // Clear the URL from the change tracker.
93    change_tracker()->ClearChangesForURL(url);
94  }
95
96  void DropChangesInTracker() {
97    change_tracker()->DropAllChanges();
98  }
99
100  void RestoreChangesFromTrackerDB() {
101    change_tracker()->CollectLastDirtyChanges(file_system_context());
102  }
103
104  ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
105  base::MessageLoop message_loop_;
106  CannedSyncableFileSystem file_system_;
107
108 private:
109  scoped_refptr<LocalFileSyncContext> sync_context_;
110
111  DISALLOW_COPY_AND_ASSIGN(LocalFileChangeTrackerTest);
112};
113
114TEST_F(LocalFileChangeTrackerTest, GetChanges) {
115  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
116
117  // Test URLs (no parent/child relationships, as we test such cases
118  // mainly in LocalFileSyncStatusTest).
119  const char kPath0[] = "test/dir a/dir";
120  const char kPath1[] = "test/dir b";
121  const char kPath2[] = "test/foo.txt";
122  const char kPath3[] = "test/bar";
123  const char kPath4[] = "temporary/dir a";
124  const char kPath5[] = "temporary/foo";
125
126  change_tracker()->OnCreateDirectory(URL(kPath0));
127  change_tracker()->OnRemoveDirectory(URL(kPath0));  // Offset the create.
128  change_tracker()->OnRemoveDirectory(URL(kPath1));
129  change_tracker()->OnCreateDirectory(URL(kPath2));
130  change_tracker()->OnRemoveFile(URL(kPath3));
131  change_tracker()->OnModifyFile(URL(kPath4));
132  change_tracker()->OnCreateFile(URL(kPath5));
133  change_tracker()->OnRemoveFile(URL(kPath5));  // Recorded as 'delete'.
134
135  FileSystemURLSet urls;
136  file_system_.GetChangedURLsInTracker(&urls);
137
138  EXPECT_EQ(5U, urls.size());
139  EXPECT_TRUE(ContainsKey(urls, URL(kPath1)));
140  EXPECT_TRUE(ContainsKey(urls, URL(kPath2)));
141  EXPECT_TRUE(ContainsKey(urls, URL(kPath3)));
142  EXPECT_TRUE(ContainsKey(urls, URL(kPath4)));
143  EXPECT_TRUE(ContainsKey(urls, URL(kPath5)));
144
145  // Changes for kPath0 must have been offset and removed.
146  EXPECT_FALSE(ContainsKey(urls, URL(kPath0)));
147
148  // GetNextChangedURLs only returns up to max_urls (i.e. 3) urls.
149  std::deque<FileSystemURL> urls_to_process;
150  change_tracker()->GetNextChangedURLs(&urls_to_process, 3);
151  ASSERT_EQ(3U, urls_to_process.size());
152
153  // Let it return all.
154  urls_to_process.clear();
155  change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
156  ASSERT_EQ(5U, urls_to_process.size());
157
158  // The changes must be in the last-modified-time order.
159  EXPECT_EQ(URL(kPath1), urls_to_process[0]);
160  EXPECT_EQ(URL(kPath2), urls_to_process[1]);
161  EXPECT_EQ(URL(kPath3), urls_to_process[2]);
162  EXPECT_EQ(URL(kPath4), urls_to_process[3]);
163  EXPECT_EQ(URL(kPath5), urls_to_process[4]);
164
165  // Modify kPath4 again.
166  change_tracker()->OnModifyFile(URL(kPath4));
167
168  // Now the order must be changed.
169  urls_to_process.clear();
170  change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
171  ASSERT_EQ(5U, urls_to_process.size());
172  EXPECT_EQ(URL(kPath1), urls_to_process[0]);
173  EXPECT_EQ(URL(kPath2), urls_to_process[1]);
174  EXPECT_EQ(URL(kPath3), urls_to_process[2]);
175  EXPECT_EQ(URL(kPath5), urls_to_process[3]);
176  EXPECT_EQ(URL(kPath4), urls_to_process[4]);
177
178  VerifyAndClearChange(URL(kPath1),
179               FileChange(FileChange::FILE_CHANGE_DELETE,
180                          sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
181  VerifyAndClearChange(URL(kPath2),
182               FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
183                          sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
184  VerifyAndClearChange(URL(kPath3),
185               FileChange(FileChange::FILE_CHANGE_DELETE,
186                          sync_file_system::SYNC_FILE_TYPE_FILE));
187  VerifyAndClearChange(URL(kPath4),
188               FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
189                          sync_file_system::SYNC_FILE_TYPE_FILE));
190  VerifyAndClearChange(URL(kPath5),
191               FileChange(FileChange::FILE_CHANGE_DELETE,
192                          sync_file_system::SYNC_FILE_TYPE_FILE));
193}
194
195TEST_F(LocalFileChangeTrackerTest, RestoreCreateAndModifyChanges) {
196  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
197
198  FileSystemURLSet urls;
199
200  const char kPath0[] = "file a";
201  const char kPath1[] = "dir a";
202  const char kPath2[] = "dir a/dir";
203  const char kPath3[] = "dir a/file a";
204  const char kPath4[] = "dir a/file b";
205
206  file_system_.GetChangedURLsInTracker(&urls);
207  ASSERT_EQ(0U, urls.size());
208
209  const std::string kData("Lorem ipsum.");
210  MockBlobURLRequestContext url_request_context(file_system_context());
211  ScopedTextBlob blob(url_request_context, "blob_id:test", kData);
212
213  // Create files and nested directories.
214  EXPECT_EQ(base::PLATFORM_FILE_OK,
215            file_system_.CreateFile(URL(kPath0)));       // Creates a file.
216  EXPECT_EQ(base::PLATFORM_FILE_OK,
217            file_system_.CreateDirectory(URL(kPath1)));  // Creates a dir.
218  EXPECT_EQ(base::PLATFORM_FILE_OK,
219            file_system_.CreateDirectory(URL(kPath2)));  // Creates another dir.
220  EXPECT_EQ(base::PLATFORM_FILE_OK,
221            file_system_.CreateFile(URL(kPath3)));       // Creates a file.
222  EXPECT_EQ(base::PLATFORM_FILE_OK,
223            file_system_.TruncateFile(URL(kPath3), 1));  // Modifies the file.
224  EXPECT_EQ(base::PLATFORM_FILE_OK,
225            file_system_.CreateFile(URL(kPath4)));    // Creates another file.
226  EXPECT_EQ(static_cast<int64>(kData.size()),         // Modifies the file.
227            file_system_.Write(&url_request_context,
228                               URL(kPath4), blob.GetBlobDataHandle()));
229
230  // Verify the changes.
231  file_system_.GetChangedURLsInTracker(&urls);
232  EXPECT_EQ(5U, urls.size());
233
234  // Reset the changes in in-memory tracker.
235  DropChangesInTracker();
236
237  // Make sure we have no in-memory changes in the tracker.
238  file_system_.GetChangedURLsInTracker(&urls);
239  ASSERT_EQ(0U, urls.size());
240
241  RestoreChangesFromTrackerDB();
242
243  // Make sure the changes are restored from the DB.
244  file_system_.GetChangedURLsInTracker(&urls);
245  EXPECT_EQ(5U, urls.size());
246
247  VerifyAndClearChange(URL(kPath0),
248                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
249                                  sync_file_system::SYNC_FILE_TYPE_FILE));
250  VerifyAndClearChange(URL(kPath1),
251                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
252                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
253  VerifyAndClearChange(URL(kPath2),
254                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
255                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
256  VerifyAndClearChange(URL(kPath3),
257                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
258                                  sync_file_system::SYNC_FILE_TYPE_FILE));
259  VerifyAndClearChange(URL(kPath4),
260                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
261                                  sync_file_system::SYNC_FILE_TYPE_FILE));
262}
263
264TEST_F(LocalFileChangeTrackerTest, RestoreRemoveChanges) {
265  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
266
267  FileSystemURLSet urls;
268
269  const char kPath0[] = "file";
270  const char kPath1[] = "dir a";
271  const char kPath2[] = "dir b";
272  const char kPath3[] = "dir b/file";
273  const char kPath4[] = "dir b/dir c";
274  const char kPath5[] = "dir b/dir c/file";
275
276  file_system_.GetChangedURLsInTracker(&urls);
277  ASSERT_EQ(0U, urls.size());
278
279  // Creates and removes a same file.
280  EXPECT_EQ(base::PLATFORM_FILE_OK,
281            file_system_.CreateFile(URL(kPath0)));
282  EXPECT_EQ(base::PLATFORM_FILE_OK,
283            file_system_.Remove(URL(kPath0), false /* recursive */));
284
285  // Creates and removes a same directory.
286  EXPECT_EQ(base::PLATFORM_FILE_OK,
287            file_system_.CreateDirectory(URL(kPath1)));
288  EXPECT_EQ(base::PLATFORM_FILE_OK,
289            file_system_.Remove(URL(kPath1), false /* recursive */));
290
291  // Creates files and nested directories, then removes the parent directory.
292  EXPECT_EQ(base::PLATFORM_FILE_OK,
293            file_system_.CreateDirectory(URL(kPath2)));
294  EXPECT_EQ(base::PLATFORM_FILE_OK,
295            file_system_.CreateFile(URL(kPath3)));
296  EXPECT_EQ(base::PLATFORM_FILE_OK,
297            file_system_.CreateDirectory(URL(kPath4)));
298  EXPECT_EQ(base::PLATFORM_FILE_OK,
299            file_system_.CreateFile(URL(kPath5)));
300  EXPECT_EQ(base::PLATFORM_FILE_OK,
301            file_system_.Remove(URL(kPath2), true /* recursive */));
302
303  file_system_.GetChangedURLsInTracker(&urls);
304  EXPECT_EQ(3U, urls.size());
305
306  DropChangesInTracker();
307
308  // Make sure we have no in-memory changes in the tracker.
309  file_system_.GetChangedURLsInTracker(&urls);
310  ASSERT_EQ(0U, urls.size());
311
312  RestoreChangesFromTrackerDB();
313
314  // Make sure the changes are restored from the DB.
315  file_system_.GetChangedURLsInTracker(&urls);
316  // Since directories to have been reverted (kPath1, kPath2, kPath4) are
317  // treated as FILE_CHANGE_DELETE, the number of changes should be 6.
318  EXPECT_EQ(6U, urls.size());
319
320  VerifyAndClearChange(URL(kPath0),
321                       FileChange(FileChange::FILE_CHANGE_DELETE,
322                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
323  VerifyAndClearChange(URL(kPath1),
324                       FileChange(FileChange::FILE_CHANGE_DELETE,
325                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
326  VerifyAndClearChange(URL(kPath2),
327                       FileChange(FileChange::FILE_CHANGE_DELETE,
328                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
329  VerifyAndClearChange(URL(kPath3),
330                       FileChange(FileChange::FILE_CHANGE_DELETE,
331                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
332  VerifyAndClearChange(URL(kPath4),
333                       FileChange(FileChange::FILE_CHANGE_DELETE,
334                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
335  VerifyAndClearChange(URL(kPath5),
336                       FileChange(FileChange::FILE_CHANGE_DELETE,
337                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
338}
339
340TEST_F(LocalFileChangeTrackerTest, RestoreCopyChanges) {
341  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
342
343  FileSystemURLSet urls;
344
345  const char kPath0[] = "file a";
346  const char kPath1[] = "dir a";
347  const char kPath2[] = "dir a/dir";
348  const char kPath3[] = "dir a/file a";
349  const char kPath4[] = "dir a/file b";
350
351  const char kPath0Copy[] = "file b";      // To be copied from kPath0
352  const char kPath1Copy[] = "dir b";       // To be copied from kPath1
353  const char kPath2Copy[] = "dir b/dir";   // To be copied from kPath2
354  const char kPath3Copy[] = "dir b/file a";  // To be copied from kPath3
355  const char kPath4Copy[] = "dir b/file b";  // To be copied from kPath4
356
357  file_system_.GetChangedURLsInTracker(&urls);
358  ASSERT_EQ(0U, urls.size());
359
360  const std::string kData("Lorem ipsum.");
361  MockBlobURLRequestContext url_request_context(file_system_context());
362  ScopedTextBlob blob(url_request_context, "blob_id:test", kData);
363
364  // Create files and nested directories.
365  EXPECT_EQ(base::PLATFORM_FILE_OK,
366            file_system_.CreateFile(URL(kPath0)));       // Creates a file.
367  EXPECT_EQ(base::PLATFORM_FILE_OK,
368            file_system_.CreateDirectory(URL(kPath1)));  // Creates a dir.
369  EXPECT_EQ(base::PLATFORM_FILE_OK,
370            file_system_.CreateDirectory(URL(kPath2)));  // Creates another dir.
371  EXPECT_EQ(base::PLATFORM_FILE_OK,
372            file_system_.CreateFile(URL(kPath3)));       // Creates a file.
373  EXPECT_EQ(base::PLATFORM_FILE_OK,
374            file_system_.TruncateFile(URL(kPath3), 1));  // Modifies the file.
375  EXPECT_EQ(base::PLATFORM_FILE_OK,
376            file_system_.CreateFile(URL(kPath4)));    // Creates another file.
377  EXPECT_EQ(static_cast<int64>(kData.size()),
378            file_system_.Write(&url_request_context,   // Modifies the file.
379                               URL(kPath4), blob.GetBlobDataHandle()));
380
381  // Verify we have 5 changes for preparation.
382  file_system_.GetChangedURLsInTracker(&urls);
383  EXPECT_EQ(5U, urls.size());
384  change_tracker()->ClearChangesForURL(URL(kPath0));
385  change_tracker()->ClearChangesForURL(URL(kPath1));
386  change_tracker()->ClearChangesForURL(URL(kPath2));
387  change_tracker()->ClearChangesForURL(URL(kPath3));
388  change_tracker()->ClearChangesForURL(URL(kPath4));
389
390  // Make sure we have no changes.
391  file_system_.GetChangedURLsInTracker(&urls);
392  EXPECT_TRUE(urls.empty());
393
394  // Copy the file and the parent directory.
395  EXPECT_EQ(base::PLATFORM_FILE_OK,
396            file_system_.Copy(URL(kPath0), URL(kPath0Copy)));  // Copy the file.
397  EXPECT_EQ(base::PLATFORM_FILE_OK,
398            file_system_.Copy(URL(kPath1), URL(kPath1Copy)));  // Copy the dir.
399
400  file_system_.GetChangedURLsInTracker(&urls);
401  EXPECT_EQ(5U, urls.size());
402  DropChangesInTracker();
403
404  // Make sure we have no in-memory changes in the tracker.
405  file_system_.GetChangedURLsInTracker(&urls);
406  ASSERT_EQ(0U, urls.size());
407
408  RestoreChangesFromTrackerDB();
409
410  // Make sure the changes are restored from the DB.
411  file_system_.GetChangedURLsInTracker(&urls);
412  EXPECT_EQ(5U, urls.size());
413
414  VerifyAndClearChange(URL(kPath0Copy),
415                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
416                                  sync_file_system::SYNC_FILE_TYPE_FILE));
417  VerifyAndClearChange(URL(kPath1Copy),
418                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
419                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
420  VerifyAndClearChange(URL(kPath2Copy),
421                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
422                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
423  VerifyAndClearChange(URL(kPath3Copy),
424                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
425                                  sync_file_system::SYNC_FILE_TYPE_FILE));
426  VerifyAndClearChange(URL(kPath4Copy),
427                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
428                                  sync_file_system::SYNC_FILE_TYPE_FILE));
429}
430
431TEST_F(LocalFileChangeTrackerTest, RestoreMoveChanges) {
432  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
433
434  FileSystemURLSet urls;
435
436  const char kPath0[] = "file a";
437  const char kPath1[] = "dir a";
438  const char kPath2[] = "dir a/file";
439  const char kPath3[] = "dir a/dir";
440  const char kPath4[] = "dir a/dir/file";
441
442  const char kPath5[] = "file b";          // To be moved from kPath0.
443  const char kPath6[] = "dir b";           // To be moved from kPath1.
444  const char kPath7[] = "dir b/file";      // To be moved from kPath2.
445  const char kPath8[] = "dir b/dir";       // To be moved from kPath3.
446  const char kPath9[] = "dir b/dir/file";  // To be moved from kPath4.
447
448  file_system_.GetChangedURLsInTracker(&urls);
449  ASSERT_EQ(0U, urls.size());
450
451  // Create files and nested directories.
452  EXPECT_EQ(base::PLATFORM_FILE_OK,
453            file_system_.CreateFile(URL(kPath0)));
454  EXPECT_EQ(base::PLATFORM_FILE_OK,
455            file_system_.CreateDirectory(URL(kPath1)));
456  EXPECT_EQ(base::PLATFORM_FILE_OK,
457            file_system_.CreateFile(URL(kPath2)));
458  EXPECT_EQ(base::PLATFORM_FILE_OK,
459            file_system_.CreateDirectory(URL(kPath3)));
460  EXPECT_EQ(base::PLATFORM_FILE_OK,
461            file_system_.CreateFile(URL(kPath4)));
462
463  // Verify we have 5 changes for preparation.
464  file_system_.GetChangedURLsInTracker(&urls);
465  EXPECT_EQ(5U, urls.size());
466  change_tracker()->ClearChangesForURL(URL(kPath0));
467  change_tracker()->ClearChangesForURL(URL(kPath1));
468  change_tracker()->ClearChangesForURL(URL(kPath2));
469  change_tracker()->ClearChangesForURL(URL(kPath3));
470  change_tracker()->ClearChangesForURL(URL(kPath4));
471
472  // Make sure we have no changes.
473  file_system_.GetChangedURLsInTracker(&urls);
474  EXPECT_TRUE(urls.empty());
475
476  // Move the file and the parent directory.
477  EXPECT_EQ(base::PLATFORM_FILE_OK,
478            file_system_.Move(URL(kPath0), URL(kPath5)));
479  EXPECT_EQ(base::PLATFORM_FILE_OK,
480            file_system_.Move(URL(kPath1), URL(kPath6)));
481
482  file_system_.GetChangedURLsInTracker(&urls);
483  EXPECT_EQ(10U, urls.size());
484
485  DropChangesInTracker();
486
487  // Make sure we have no in-memory changes in the tracker.
488  file_system_.GetChangedURLsInTracker(&urls);
489  ASSERT_EQ(0U, urls.size());
490
491  RestoreChangesFromTrackerDB();
492
493  // Make sure the changes are restored from the DB.
494  file_system_.GetChangedURLsInTracker(&urls);
495  // Deletion for child files in the deleted directory cannot be restored,
496  // so we will only have 8 changes.
497  EXPECT_EQ(8U, urls.size());
498
499  VerifyAndClearChange(URL(kPath0),
500                       FileChange(FileChange::FILE_CHANGE_DELETE,
501                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
502  VerifyAndClearChange(URL(kPath1),
503                       FileChange(FileChange::FILE_CHANGE_DELETE,
504                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
505  VerifyAndClearChange(URL(kPath3),
506                       FileChange(FileChange::FILE_CHANGE_DELETE,
507                                  sync_file_system::SYNC_FILE_TYPE_UNKNOWN));
508  VerifyAndClearChange(URL(kPath5),
509                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
510                                  sync_file_system::SYNC_FILE_TYPE_FILE));
511  VerifyAndClearChange(URL(kPath6),
512                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
513                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
514  VerifyAndClearChange(URL(kPath7),
515                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
516                                  sync_file_system::SYNC_FILE_TYPE_FILE));
517  VerifyAndClearChange(URL(kPath8),
518                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
519                                  sync_file_system::SYNC_FILE_TYPE_DIRECTORY));
520  VerifyAndClearChange(URL(kPath9),
521                       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
522                                  sync_file_system::SYNC_FILE_TYPE_FILE));
523}
524
525TEST_F(LocalFileChangeTrackerTest, NextChangedURLsWithRecursiveCopy) {
526  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
527
528  FileSystemURLSet urls;
529
530  const char kPath0[] = "dir a";
531  const char kPath1[] = "dir a/file";
532  const char kPath2[] = "dir a/dir";
533
534  const char kPath0Copy[] = "dir b";
535  const char kPath1Copy[] = "dir b/file";
536  const char kPath2Copy[] = "dir b/dir";
537
538  // Creates kPath0,1,2 and then copies them all.
539  EXPECT_EQ(base::PLATFORM_FILE_OK,
540            file_system_.CreateDirectory(URL(kPath0)));
541  EXPECT_EQ(base::PLATFORM_FILE_OK,
542            file_system_.CreateFile(URL(kPath1)));
543  EXPECT_EQ(base::PLATFORM_FILE_OK,
544            file_system_.CreateDirectory(URL(kPath2)));
545  EXPECT_EQ(base::PLATFORM_FILE_OK,
546            file_system_.Copy(URL(kPath0), URL(kPath0Copy)));
547
548  std::deque<FileSystemURL> urls_to_process;
549  change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
550  ASSERT_EQ(6U, urls_to_process.size());
551
552  // Creation must have occured first.
553  EXPECT_EQ(URL(kPath0), urls_to_process[0]);
554  EXPECT_EQ(URL(kPath1), urls_to_process[1]);
555  EXPECT_EQ(URL(kPath2), urls_to_process[2]);
556
557  // Then recursive copy took place. The exact order cannot be determined
558  // but the parent directory must have been created first.
559  EXPECT_EQ(URL(kPath0Copy), urls_to_process[3]);
560  EXPECT_TRUE(URL(kPath1Copy) == urls_to_process[4] ||
561              URL(kPath2Copy) == urls_to_process[4]);
562  EXPECT_TRUE(URL(kPath1Copy) == urls_to_process[5] ||
563              URL(kPath2Copy) == urls_to_process[5]);
564}
565
566TEST_F(LocalFileChangeTrackerTest, NextChangedURLsWithRecursiveRemove) {
567  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem());
568
569  FileSystemURLSet urls;
570
571  const char kPath0[] = "dir a";
572  const char kPath1[] = "dir a/file1";
573  const char kPath2[] = "dir a/file2";
574
575  // Creates kPath0,1,2 and then removes them all.
576  EXPECT_EQ(base::PLATFORM_FILE_OK,
577            file_system_.CreateDirectory(URL(kPath0)));
578  EXPECT_EQ(base::PLATFORM_FILE_OK,
579            file_system_.CreateFile(URL(kPath1)));
580  EXPECT_EQ(base::PLATFORM_FILE_OK,
581            file_system_.CreateFile(URL(kPath2)));
582  EXPECT_EQ(base::PLATFORM_FILE_OK,
583            file_system_.Remove(URL(kPath0), true /* recursive */));
584
585  std::deque<FileSystemURL> urls_to_process;
586  change_tracker()->GetNextChangedURLs(&urls_to_process, 0);
587
588  // This is actually not really desirable, but since the directory
589  // creation and deletion have been offset now we only have two
590  // file deletion changes.
591  //
592  // NOTE: This will cause 2 local sync for deleting nonexistent files
593  // on the remote side.
594  //
595  // TODO(kinuko): For micro optimization we could probably restore the ADD
596  // change type (other than ADD_OR_UPDATE) and offset file ADD+DELETE
597  // changes too.
598  ASSERT_EQ(2U, urls_to_process.size());
599
600  // The exact order of recursive removal cannot be determined.
601  EXPECT_TRUE(URL(kPath1) == urls_to_process[0] ||
602              URL(kPath2) == urls_to_process[0]);
603  EXPECT_TRUE(URL(kPath1) == urls_to_process[1] ||
604              URL(kPath2) == urls_to_process[1]);
605}
606
607}  // namespace sync_file_system
608