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